/*
 * Copyright [2020] [MaxKey of copyright http://www.maxkey.top]
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *     http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
 

package org.maxkey.crypto;

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.Base64;
import javax.crypto.Cipher;

import org.apache.commons.lang3.StringUtils;


/**
 * @author shiming
 *
 */
public final class RSAUtils {
	
	public static final String 		KEY_ALGORTHM 		= 	"RSA";
	
	public static final String 		LINE_SEPARATOR 		= 	"\n";
	
	public static final int 		KEY_SIZE			= 	1024;
	
	public static final int 		PEM_ARRAY_SIZE		= 	64;
	
	/**
	 * gen RSA KeyPair 
	 * @return KeyPair
	 * @throws Exception
	 */
	public static KeyPair genRSAKeyPair() throws Exception {
		KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(KEY_ALGORTHM);
		keyPairGenerator.initialize(KEY_SIZE);
		return keyPairGenerator.generateKeyPair();
	}
	
	/**
	 * 通过keyBytes构建私钥
	 * @param keyBytes
	 * @return
	 * @throws Exception
	 */
	public static PrivateKey privateKey(byte[] keyBytes)throws Exception {
		PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(keyBytes);
		KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORTHM);
		return  keyFactory.generatePrivate(pkcs8EncodedKeySpec);
	}
	
	/**
	 * 通过keyBytes构建公钥
	 * @param keyBytes
	 * @return
	 * @throws Exception
	 */
	public static PublicKey publicKey(byte[] keyBytes)throws Exception {
		X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(keyBytes);
		KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORTHM);
		return keyFactory.generatePublic(x509EncodedKeySpec);
	}
	
	/**
	 * 私钥加密
	 * @param data  明文数据
	 * @param hexKey  私钥HEX编码
	 * @return
	 * @throws Exception
	 */
	public static byte[] encryptByPrivateKey(byte[] data, String hexKey)throws Exception {
		byte[] keyBytes = HexUtils.hex2Bytes(hexKey);
		return encryptByPrivateKey(data,keyBytes);
	}
	
	/**
	 * 私钥加密
	 * @param data  明文数据
	 * @param hexKey  私钥
	 * @return
	 * @throws Exception
	 */
	public static byte[] encryptByPrivateKey(byte[] data, byte[] keyBytes)throws Exception {
		KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORTHM);
		Key privateKey = privateKey(keyBytes);

		Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
		cipher.init(Cipher.ENCRYPT_MODE, privateKey);

		return cipher.doFinal(data);
	}
	


	/**
	 * 私钥解密
	 * @param data  解密数据
	 * @param hexKey  私钥HEX编码
	 * @return 明文数据
	 * @throws Exception
	 */
	public static byte[] decryptByPrivateKey(byte[] data, String hexKey)throws Exception {
		// 私钥HEX编码转换为byte
		byte[] keyBytes = HexUtils.hex2Bytes(hexKey);
		
		return decryptByPrivateKey(data,keyBytes);
	}
	
	/**
	 * 私钥解密
	 * @param data 解密数据
	 * @param keyBytes 私钥
	 * @return 明文数据
	 * @throws Exception
	 */
	public static byte[] decryptByPrivateKey(byte[] data, byte[] keyBytes)throws Exception {
		KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORTHM);
		Key privateKey = privateKey(keyBytes);
		// 解密
		Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
		cipher.init(Cipher.DECRYPT_MODE, privateKey);

		return cipher.doFinal(data);
	}

	/**
	 * 公钥解密
	 * @param data 明文数据
	 * @param hexKey 公钥HEX
	 * @return 密文
	 * @throws Exception
	 */
	public static byte[] encryptByPublicKey(byte[] data, String hexKey)throws Exception {
		// �Թ�Կ����
		byte[] keyBytes = HexUtils.hex2Bytes(hexKey);
		return encryptByPublicKey(data,keyBytes);
	}
	
	/**
	 * 公钥解密
	 * @param data 明文数据
	 * @param hexKey 公钥
	 * @return 密文
	 * @throws Exception
	 */
	public static byte[] encryptByPublicKey(byte[] data, byte[] keyBytes)throws Exception {
		KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORTHM);
		Key publicKey = publicKey(keyBytes);

		// ����ݽ���
		Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
		cipher.init(Cipher.ENCRYPT_MODE, publicKey);

		return cipher.doFinal(data);
	}

	/**
	 * 公钥解密
	 * @param data 密文数据
	 * @param hexKey 公钥HEX
	 * @return 明文
	 * @throws Exception
	 */
	public static byte[] decryptByPublicKey(byte[] data, String hexKey)throws Exception {
		// hexKey 公钥HEX转换为byte
		byte[] keyBytes = HexUtils.hex2Bytes(hexKey);
		return decryptByPublicKey(data,keyBytes);
	}
	
	/**
	 * 公钥解密
	 * @param data 密文数据
	 * @param keyBytes 公钥
	 * @return 明文
	 * @throws Exception
	 */
	public static byte[] decryptByPublicKey(byte[] data, byte[] keyBytes)throws Exception {
		
		KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORTHM);
		Key publicKey = publicKey(keyBytes);

		// 解密
		Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
		cipher.init(Cipher.DECRYPT_MODE, publicKey);

		return cipher.doFinal(data);
	}
	
	public static  byte[]  sign(byte[] src, RSAPrivateKey privateKey, String algorithm) {
		if(StringUtils.isBlank(algorithm)) {
			algorithm = "SHA1withRSA";
		}
		try {
			Signature signature = Signature.getInstance(algorithm);
			signature.initSign(privateKey);
			signature.update(src);
			return signature.sign();
		} catch (Exception e) {
			e.printStackTrace();
			return null;
		}
	}
	
	public static boolean verify(byte[] sign, byte[] src, RSAPublicKey publicKey, String algorithm) {
		try {
			if(StringUtils.isBlank(algorithm)) {
				algorithm = "SHA1withRSA";
			}
			
			Signature signature = Signature.getInstance(algorithm);
			signature.initVerify(publicKey);
			signature.update(src);
			return signature.verify(sign);
		} catch (Exception e) {
			e.printStackTrace();
			return false;
		}
	}
	
	/**
	 * 获取公钥的PEM格式
	 * @param encoded 公钥
	 * @return PEM格式公钥
	 */
	public static String getPublicKeyPEM(byte[] encoded) {
		StringBuffer base64String = 
				new StringBuffer("");
		base64String.append("-----BEGIN PUBLIC KEY-----").append(LINE_SEPARATOR);
		base64String.append(getBase64PEM(encoded)).append(LINE_SEPARATOR);
		base64String.append("-----END PUBLIC KEY-----").append(LINE_SEPARATOR);
		return base64String.toString();
	}
	
	/**
	 * 获取私钥的PEM格式
	 * @param encoded 私钥
	 * @return PEM格式私钥
	 */
	public static String getPrivateKeyPEM(byte[] encoded) {
		StringBuffer base64String = 
				new StringBuffer("");
		base64String.append("-----BEGIN RSA PRIVATE KEY-----").append(LINE_SEPARATOR);
		base64String.append(getBase64PEM(encoded)).append(LINE_SEPARATOR);
		base64String.append("-----END RSA PRIVATE KEY-----").append(LINE_SEPARATOR);
		return base64String.toString();
	}
	
	/**
	 * 获取Certificate的PEM格式
	 * @param encoded 公钥
	 * @return PEM格式公钥
	 */
	public static String getCertificatePEM(byte[] encoded) {
		StringBuffer base64String = 
				new StringBuffer("");
		base64String.append("-----BEGIN CERTIFICATE-----").append(LINE_SEPARATOR);
		base64String.append(getBase64PEM(encoded)).append(LINE_SEPARATOR);
		base64String.append("-----END CERTIFICATE-----").append(LINE_SEPARATOR);
		return base64String.toString();
	}
	
	/**
	 * 获取密钥的PEM格式
	 * @param encoded 密钥
	 * @return PEM格式密钥
	 */
	public static String getBase64PEM(byte[] encoded) {
		String base64String = 
				Base64.getMimeEncoder(PEM_ARRAY_SIZE,LINE_SEPARATOR.getBytes()).encodeToString(encoded);
		//StringBuffer base64ArrayString = new StringBuffer("");
		//int startPosition = 0;
		//int endPosition = PEM_ARRAY_SIZE;
		//while(endPosition < base64String.length()) {
		//	base64ArrayString.append(base64String.substring(startPosition, endPosition)).append("\n");
		//	startPosition = endPosition;
		//	endPosition = endPosition + PEM_ARRAY_SIZE;
		//}
		//if(startPosition < base64String.length()) {
		//	base64ArrayString.append(base64String.substring(startPosition)).append("\n");
		//}
		
		//return base64ArrayString.toString();
		return base64String;
	}
}
