package jj.tech.paolu.utils;

import java.io.ByteArrayOutputStream;
import java.security.Key;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.Signature;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.HashMap;
import java.util.Map;

import javax.crypto.Cipher;

import org.apache.commons.codec.binary.Base64;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
 * <p>
 * RSA公钥/私钥/签名工具包
 * </p>
 * <p>
 * 罗纳德·李维斯特（Ron [R]ivest）、阿迪·萨莫尔（Adi [S]hamir）和伦纳德·阿德曼（Leonard [A]dleman）
 * </p>
 * <p>
 * 字符串格式的密钥在未在特殊说明情况下都为BASE64编码格式<br/>
 * 由于非对称加密速度极其缓慢，一般文件不使用它来加密而是使用对称加密，<br/>
 * 非对称加密算法可以用来对对称加密的密钥加密，这样保证密钥的安全也就保证了数据的安全
 * </p>
 * 
 * @author XDou
 * @date 2017-11-16
 */
public class RSA {
	private Logger log = LoggerFactory.getLogger(RSA.class);
	
	public static RSA getInstance() {
		return new RSA();
	}
	/**
	 * 加密算法RSA
	 */
	public static final String rsa = "RSA";

	/**
	 * 签名算法
	 */
	public static final String SHA1withRSA = "SHA1withRSA";
	public static final String SHA256withRSA = "SHA256withRSA";
	

	/**
	 * RSA最大加密明文大小
	 */
	private static final int MAX_ENCRYPT_BLOCK = 117;

	/**
	 * RSA最大解密密文大小
	 */
	private static final int MAX_DECRYPT_BLOCK = 128;

	/**
	 * 生成密钥对(公钥和私钥)
	 */
	public String[] genKeyPair() {
		try {
			KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance(rsa);
			keyPairGen.initialize(1024);
			KeyPair keyPair = keyPairGen.generateKeyPair();
			RSAPublicKey rsa_public = (RSAPublicKey) keyPair.getPublic();
			RSAPrivateKey rsa_private = (RSAPrivateKey) keyPair.getPrivate();
			
			String public_key = Base64.encodeBase64String(rsa_public.getEncoded());
			String private_key = Base64.encodeBase64String(rsa_private.getEncoded());
			return new String[] {public_key, private_key};
		} catch (Exception e) {
			log.error("", e);
			return null;
		}
	}

	/**
	 * 用私钥对信息生成数字签名
	 * @param src 待签名的源数据
	 * @param privateKey 私钥(BASE64编码)
	 * @param algorithm 生存签名的算法
	 */
	public String sign(String src, String privateKey, String algorithm) {
		try {
			byte[] data = src.getBytes("utf-8");
			byte[] keyBytes = Base64.decodeBase64(privateKey);
			PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);
			KeyFactory keyFactory = KeyFactory.getInstance(rsa);
			PrivateKey privateK = keyFactory.generatePrivate(pkcs8KeySpec);
			Signature signature = Signature.getInstance(algorithm);
			signature.initSign(privateK);
			signature.update(data);
			return Base64.encodeBase64String(signature.sign());
		} catch (Exception e) {
			log.error("", e);
			return null;
		}
	}
	

	/**
	 * 用公钥，校验数字签名sign的值，是否正确
	 * @param src 待签名源数据
	 * @param publicKey 公钥(BASE64编码)
	 * @param sign 数字签名
	 * @param algorithm 生成签名的算法
	 */
	public boolean verify(String src, String publicKey, String sign, String algorithm) {
		try {
			byte[] data = src.getBytes("utf-8");
			byte[] keyBytes = Base64.decodeBase64(publicKey);
			X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes);
			KeyFactory keyFactory = KeyFactory.getInstance(rsa);
			PublicKey publicK = keyFactory.generatePublic(keySpec);
			Signature signature = Signature.getInstance(algorithm);
			signature.initVerify(publicK);
			signature.update(data);
			return signature.verify(Base64.decodeBase64(sign));
		} catch (Exception e) {
			log.error("", e);
			return false;
		}

	}
	
	
	
	
	/**
	 * 私钥加密，返回base64编码
	 * @param data 源数据
	 * @param privateKey  私钥(BASE64编码)
	 */
	public String encryptByPrivateKey(String src, String privateKey) {
		try {
			byte[] data = src.getBytes("utf-8");
			byte[] keyBytes = Base64.decodeBase64(privateKey);
			PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);
			KeyFactory keyFactory = KeyFactory.getInstance(rsa);
			Key privateK = keyFactory.generatePrivate(pkcs8KeySpec);
			Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
			cipher.init(Cipher.ENCRYPT_MODE, privateK);
			int inputLen = data.length;
			ByteArrayOutputStream out = new ByteArrayOutputStream();
			int offSet = 0;
			byte[] cache;
			int i = 0;
			// 对数据分段加密
			while (inputLen - offSet > 0) {
				if (inputLen - offSet > MAX_ENCRYPT_BLOCK) {
					cache = cipher.doFinal(data, offSet, MAX_ENCRYPT_BLOCK);
				} else {
					cache = cipher.doFinal(data, offSet, inputLen - offSet);
				}
				out.write(cache, 0, cache.length);
				i++;
				offSet = i * MAX_ENCRYPT_BLOCK;
			}
			byte[] encryptedData = out.toByteArray();
			out.close();

			return Base64.encodeBase64String(encryptedData);

		} catch (Exception e) {
			log.error("", e);
			return null;
		}
	}
	
	/**
	 * 私钥解密
	 * @param encryptedSrc 已加密数据
	 * @param privateKey 私钥(BASE64编码)
	 */
	public String decryptByPrivateKey(String encryptedSrc, String privateKey) {
		try {
			byte[] encryptedData = Base64.decodeBase64(encryptedSrc);
			byte[] keyBytes = Base64.decodeBase64(privateKey);
			PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);
			KeyFactory keyFactory = KeyFactory.getInstance(rsa);
			Key privateK = keyFactory.generatePrivate(pkcs8KeySpec);
			Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
			cipher.init(Cipher.DECRYPT_MODE, privateK);
			int inputLen = encryptedData.length;
			ByteArrayOutputStream out = new ByteArrayOutputStream();
			int offSet = 0;
			byte[] cache;
			int i = 0;
			// 对数据分段解密
			while (inputLen - offSet > 0) {
				if (inputLen - offSet > MAX_DECRYPT_BLOCK) {
					cache = cipher.doFinal(encryptedData, offSet, MAX_DECRYPT_BLOCK);
				} else {
					cache = cipher.doFinal(encryptedData, offSet, inputLen - offSet);
				}
				out.write(cache, 0, cache.length);
				i++;
				offSet = i * MAX_DECRYPT_BLOCK;
			}
			byte[] decryptedData = out.toByteArray();
			out.close();
			return new String(decryptedData, "utf-8");

		} catch (Exception e) {
			log.error("", e);
			return null;
		}
	}


	/**
	 * 公钥加密
	 * @param data 源数据
	 * @param publicKey 公钥(BASE64编码)
	 */
	public String encryptByPublicKey(String src, String publicKey) {
		try {
			byte[] data = src.getBytes("utf-8");
			byte[] keyBytes = Base64.decodeBase64(publicKey);
			X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(keyBytes);
			KeyFactory keyFactory = KeyFactory.getInstance(rsa);
			Key publicK = keyFactory.generatePublic(x509KeySpec);
			// 对数据加密
			Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
			cipher.init(Cipher.ENCRYPT_MODE, publicK);
			int inputLen = data.length;
			ByteArrayOutputStream out = new ByteArrayOutputStream();
			int offSet = 0;
			byte[] cache;
			int i = 0;
			// 对数据分段加密
			while (inputLen - offSet > 0) {
				if (inputLen - offSet > MAX_ENCRYPT_BLOCK) {
					cache = cipher.doFinal(data, offSet, MAX_ENCRYPT_BLOCK);
				} else {
					cache = cipher.doFinal(data, offSet, inputLen - offSet);
				}
				out.write(cache, 0, cache.length);
				i++;
				offSet = i * MAX_ENCRYPT_BLOCK;
			}
			byte[] encryptedData = out.toByteArray();
			out.close();
			return Base64.encodeBase64String(encryptedData);
		} catch (Exception e) {
			log.error("", e);
			return null;
		}
	}

	/**
	 * 公钥解密
	 * @param encryptedData 已加密数据
	 * @param publicKey 公钥(BASE64编码)
	 */
	public String decryptByPublicKey(String encryptedSrc, String publicKey) {
		try {
			byte[] encryptedData = Base64.decodeBase64(encryptedSrc);
			byte[] keyBytes = Base64.decodeBase64(publicKey);
			X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(keyBytes);
			KeyFactory keyFactory = KeyFactory.getInstance(rsa);
			Key publicK = keyFactory.generatePublic(x509KeySpec);
			Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
			cipher.init(Cipher.DECRYPT_MODE, publicK);
			int inputLen = encryptedData.length;
			ByteArrayOutputStream out = new ByteArrayOutputStream();
			int offSet = 0;
			byte[] cache;
			int i = 0;
			// 对数据分段解密
			while (inputLen - offSet > 0) {
				if (inputLen - offSet > MAX_DECRYPT_BLOCK) {
					cache = cipher.doFinal(encryptedData, offSet, MAX_DECRYPT_BLOCK);
				} else {
					cache = cipher.doFinal(encryptedData, offSet, inputLen - offSet);
				}
				out.write(cache, 0, cache.length);
				i++;
				offSet = i * MAX_DECRYPT_BLOCK;
			}
			byte[] decryptedData = out.toByteArray();
			out.close();
			return new String(decryptedData, "utf-8");

		} catch (Exception e) {
			log.error("", e);
			return null;
		}
	}
	
	
	
	/**
	 * 默认使用SHA1withRSA签名
	 */
	public String sign(String src, String privateKey) {
		return this.sign(src, privateKey, SHA1withRSA);
	}
	
	/**
	 * 默认使用SHA1withRS验证签名是否正确
	 */
	public boolean verify(String src, String publicKey, String sign) {
		return this.verify(src, publicKey, sign, SHA1withRSA);
	}

//	public static void main(String[] args) {
//		RSA rsa = RSA.getInstance();
//		String[] obj = rsa.genKeyPair();
//		
//		String publicKey = obj[0];
//		String privateKey = obj[1];
//		
//		System.err.println("privatekey:");
//		System.out.println("" + privateKey);
//		
//		System.err.println("publickey:");
//		System.out.println(publicKey);
//		
//		
//		String src = "aaa";
//		//签名
//		String sign = rsa.sign(src, privateKey);
//		Boolean b = rsa.verify(src, publicKey, sign);
//		System.err.println("签名：");
//		System.out.println(sign);
//		System.err.println(b);
//		
//		//签名sha256
//		String sign256 = rsa.sign(src, privateKey, SHA256withRSA);
//		Boolean v = rsa.verify(src, publicKey, sign256, SHA256withRSA);
//		System.err.println("签名sign256：");
//		System.out.println(sign256);
//		System.err.println(v);
//		
//		
//		
//		//私钥加密
//		String enc = rsa.encryptByPrivateKey(src, privateKey);
//		System.out.println("私钥加密:"+ enc);
//		System.out.println("公钥解密:" + rsa.decryptByPublicKey(enc, publicKey));
//		
//		//公钥加密
//		String enc2 = rsa.encryptByPublicKey(src, publicKey);
//		System.out.println("公钥加密:"+ enc2);
//		System.out.println("私钥解密:" + rsa.decryptByPrivateKey(enc2, privateKey));
//	}

}