package net.yyc.common.ding.utils;

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.binary.Base64;

import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
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;

@Slf4j
public enum RsaCoreUtil {

	SHA1_SIGN_RSA_ENCRY("SHA1WithRSA", "RSA"),

	SHA1_SIGN_RSA_PKCS1Padding_ENCRY("SHA1WithRSA", "RSA/ECB/PKCS1Padding");

	RsaCoreUtil(String signAlgorithms, String algorithm){
		this.signAlgorithms = signAlgorithms;
		this.algorithm = algorithm;
	}



	private static final String KEY_ALGORITHM = "RSA";

	private  String algorithm = "RSA";
	private  String signAlgorithms = "SHA1WithRSA";


	/**
	 * 从字符串中加载公钥
	 *
	 * @param publicKeyStr
	 *            公钥数据字符串
	 * @throws Exception
	 *             加载公钥时产生的异常
	 */
	private RSAPublicKey getPublicKey(String publicKeyStr) throws Exception {
		try {
			byte[] buffer = Base64.decodeBase64(publicKeyStr);
			KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
			X509EncodedKeySpec keySpec = new X509EncodedKeySpec(buffer);
			RSAPublicKey publicKey = (RSAPublicKey) keyFactory
					.generatePublic(keySpec);
			return publicKey;
		} catch (NoSuchAlgorithmException e) {
			throw new Exception("无此算法");
		} catch (InvalidKeySpecException e) {
			throw new Exception("公钥非法");
		}  catch (NullPointerException e) {
			throw new Exception("公钥数据为空");
		}
	}

	/**
	 * 从字符串中加载私钥
	 *
	 * @param privateKeyStr
	 *            公钥数据字符串
	 * @throws Exception
	 *             加载私钥时产生的异常
	 */
	private RSAPrivateKey getPrivateKey(String privateKeyStr) throws Exception {
		try {
			byte[] buffer = Base64.decodeBase64(privateKeyStr);
			PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(buffer);
			KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
			RSAPrivateKey privateKey = (RSAPrivateKey) keyFactory
					.generatePrivate(keySpec);
			return privateKey;
		} catch (NoSuchAlgorithmException e) {
			throw new Exception("无此算法");
		} catch (InvalidKeySpecException e) {
			throw new Exception("私钥非法");

		} catch (NullPointerException e) {
			throw new Exception("私钥数据为空");
		}

	}

	/**
	 * 数据签名
	 *
	 *            签名内容
	 *            私钥
	 * @return 返回签名数据
	 */
	public final byte[] sign(byte[] plainBytes, String privateKeyStr) {
		try {
			PrivateKey priKey = getPrivateKey(privateKeyStr);

			return sign(plainBytes, priKey);
		} catch (Exception e) {
			log.info("sign with exception, e : " + e.toString());
			throw new RuntimeException(e);
		}

	}


	/**
	 * 数据签名
	 *
	 *            签名内容
	 *            私钥
	 * @return 返回签名数据
	 */
	public final byte[] sign(byte[] plainBytes, PrivateKey priKey) {
		try {
			java.security.Signature signature = java.security.Signature
					.getInstance(signAlgorithms);
			signature.initSign(priKey);
			signature.update(plainBytes);
			return signature.sign();
		} catch (Exception e) {
			log.info("sign with exception, e : " + e.toString(),e);
			throw new RuntimeException(e);
		}

	}

	/**
	 * 签名验证
	 *
	 * @return
	 */
	public final boolean verify(byte[] plainBytes, byte[] signBytes,
								String publicKeyStr) {

		try {
			PublicKey pubKey = getPublicKey(publicKeyStr);
			return verify(plainBytes, signBytes, pubKey);

		} catch (Exception e) {
			log.info("verify with exception, e : " + e.toString());
			throw new RuntimeException(e);
		}
	}


	/**
	 * 签名验证
	 *
	 * @return
	 */
	public final boolean verify(byte[] plainBytes, byte[] signBytes,
								PublicKey publicKey) {
		try {

			java.security.Signature signature = java.security.Signature
					.getInstance(signAlgorithms);
			signature.initVerify(publicKey);
			signature.update(plainBytes);
			boolean bverify = signature.verify(signBytes);

			log.debug("in the verify, content : " + plainBytes + ", with the singned : " + signBytes + ", result : " + bverify);
			return bverify;

		} catch (Exception e) {
			log.info("verify with exception, e : " + e.toString());
			throw new RuntimeException(e);
		}
	}


}
