using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;
using XF.Domain.Exceptions;

namespace Hotline.Application.Tools
{
    /// <summary>
    /// 加密、解密
    /// </summary>
    public class RSA
    {
        /// <summary>
        /// RSA加密
        /// </summary>
        /// <param name="Data">原文</param>
        /// <param name="PublicKeyString">公钥</param>
        /// <param name="KeyType">密钥类型XML/PEM</param>
        /// <returns></returns>
        public static string RSAEncrypt(string Data, string PublicKeyString, string KeyType)
        {
            byte[] data = Encoding.GetEncoding("UTF-8").GetBytes(Data);
            RSACryptoServiceProvider rsa = new RSACryptoServiceProvider();
            switch (KeyType)
            {
                case "XML":
                    rsa.FromXmlString(PublicKeyString);
                    break;
                case "PEM":
                    rsa = RSA_PEM.FromPEM(PublicKeyString);
                    break;
                default:
                    throw UserFriendlyException.SameMessage("不支持的密钥类型");
            }
            //加密块最大长度限制，如果加密数据的长度超过 秘钥长度/8-11，会引发长度不正确的异常，所以进行数据的分块加密
            int MaxBlockSize = rsa.KeySize / 8 - 11;
            //正常长度
            if (data.Length <= MaxBlockSize)
            {
                byte[] hashvalueEcy = rsa.Encrypt(data, false); //加密
                return System.Convert.ToBase64String(hashvalueEcy);
            }
            //长度超过正常值
            else
            {
                using (MemoryStream PlaiStream = new MemoryStream(data))
                using (MemoryStream CrypStream = new MemoryStream())
                {
                    Byte[] Buffer = new Byte[MaxBlockSize];
                    int BlockSize = PlaiStream.Read(Buffer, 0, MaxBlockSize);
                    while (BlockSize > 0)
                    {
                        Byte[] ToEncrypt = new Byte[BlockSize];
                        Array.Copy(Buffer, 0, ToEncrypt, 0, BlockSize);

                        Byte[] Cryptograph = rsa.Encrypt(ToEncrypt, false);
                        CrypStream.Write(Cryptograph, 0, Cryptograph.Length);
                        BlockSize = PlaiStream.Read(Buffer, 0, MaxBlockSize);
                    }
                    return System.Convert.ToBase64String(CrypStream.ToArray(), Base64FormattingOptions.None);
                }
            }
        }

        /// <summary>
        /// RSA解密
        /// </summary>
        /// <param name="Data">密文</param>
        /// <param name="PrivateKeyString">私钥</param>
        /// <param name="KeyType">密钥类型XML/PEM</param>
        /// <returns></returns>
        public static string RSADecrypt(string Data, string PrivateKeyString, string KeyType)
        {
            // 把+号，替换回来
            Data = Data.Replace("%2B", "+");

            // 补全密文
            Data = CorrectionCiphertext(Data);

            RSACryptoServiceProvider rsa = new RSACryptoServiceProvider();
            switch (KeyType)
            {
                case "XML":
                    rsa.FromXmlString(PrivateKeyString);
                    break;
                case "PEM":
                    rsa = RSA_PEM.FromPEM(PrivateKeyString);
                    break;
                default:
                    throw UserFriendlyException.SameMessage("不支持的密钥类型");
            }
            int MaxBlockSize = rsa.KeySize / 8;    //解密块最大长度限制
            //正常解密
            if (Data.Length <= MaxBlockSize)
            {
                byte[] hashvalueDcy = rsa.Decrypt(System.Convert.FromBase64String(Data), false);//解密
                return Encoding.GetEncoding("UTF-8").GetString(hashvalueDcy);
            }
            //分段解密
            else
            {
                using (MemoryStream CrypStream = new MemoryStream(System.Convert.FromBase64String(Data)))
                using (MemoryStream PlaiStream = new MemoryStream())
                {
                    Byte[] Buffer = new Byte[MaxBlockSize];
                    int BlockSize = CrypStream.Read(Buffer, 0, MaxBlockSize);

                    while (BlockSize > 0)
                    {
                        Byte[] ToDecrypt = new Byte[BlockSize];
                        Array.Copy(Buffer, 0, ToDecrypt, 0, BlockSize);

                        Byte[] Plaintext = rsa.Decrypt(ToDecrypt, false);
                        PlaiStream.Write(Plaintext, 0, Plaintext.Length);
                        BlockSize = CrypStream.Read(Buffer, 0, MaxBlockSize);
                    }
                    string output = Encoding.GetEncoding("UTF-8").GetString(PlaiStream.ToArray());
                    return output;
                }
            }
        }

        /// <summary>
        /// 补全密文
        /// </summary>
        /// <param name="strCiphertext">密文</param>
        /// <param name="keySize">秘钥长度</param>
        /// <returns>补全后的密文</returns>
        private static string CorrectionCiphertext(string strCiphertext, int keySize = 1024)
        {
            int ciphertextLength = keySize / 8;
            byte[] data = Convert.FromBase64String(strCiphertext);
            var newData = new List<byte>(data);
            while (newData.Count < ciphertextLength)
            {
                newData.Insert(0, 0x00);
            }
            return Convert.ToBase64String(newData.ToArray());
        }
    }
}
