using System;
using System.IO;
using System.Security.Cryptography;
using System.Text;
namespace TestProject
{
/// <summary>
/// 보안 헬퍼
/// </summary>
public static class SecurityHelper
{
//////////////////////////////////////////////////////////////////////////////////////////////////// Field
////////////////////////////////////////////////////////////////////////////////////////// Static
//////////////////////////////////////////////////////////////////////////////// Private
#region Field
/// <summary>
/// RNG 암호 서비스 공급자
/// </summary>
private static RNGCryptoServiceProvider _provider = new RNGCryptoServiceProvider();
#endregion
//////////////////////////////////////////////////////////////////////////////////////////////////// Method
////////////////////////////////////////////////////////////////////////////////////////// Static
//////////////////////////////////////////////////////////////////////////////// Public
#region 문자열로 암호화하기 - EncryptToString(source, password, salt)
/// <summary>
/// 문자열로 암호화하기
/// </summary>
/// <param name="source">소스 문자열</param>
/// <param name="password">패스워드</param>
/// <param name="salt">솔트</param>
/// <returns>암호화 문자열</returns>
public static string EncryptToString(string source, string password, string salt)
{
return GetHexadecimalString(Encrypt(source, password, salt));
}
#endregion
#region 문자열에서 복호화하기 - DecryptFromString(source, password, salt)
/// <summary>
/// 문자열에서 복호화하기
/// </summary>
/// <param name="source">소스 문자열</param>
/// <param name="password">패스워드</param>
/// <param name="salt">솔트</param>
/// <returns>복호화 문자열</returns>
public static string DecryptFromString(string source, string password, string salt)
{
return Decrypt(GetByteArrayFromHexadecimalString(source), password, salt);
}
#endregion
#region 임의 정수 값 구하기 - GetRandomIntegerValue(minimumValue, maximumValue)
/// <summary>
/// 임의 정수 값 구하기
/// </summary>
/// <param name="minimumValue">최소 값</param>
/// <param name="maximumValue">최대 값</param>
/// <returns>임의 정수 값</returns>
public static int GetRandomIntegerValue(int minimumValue, int maximumValue)
{
uint scale = uint.MaxValue;
while(scale == uint.MaxValue)
{
byte[] byteArray = new byte[4];
_provider.GetBytes(byteArray);
scale = BitConverter.ToUInt32(byteArray, 0);
}
return (int)(minimumValue + (maximumValue - minimumValue) * (scale / (double)uint.MaxValue));
}
#endregion
#region 임의 솔트 구하기 - GetRandomSalt()
/// <summary>
/// 임의 솔트 구하기
/// </summary>
/// <returns>임의 솔트</returns>
public static string GetRandomSalt()
{
int byteArrayLength = GetRandomIntegerValue(10, 20);
byte[] saltByteArray = new byte[byteArrayLength];
_provider.GetBytes(saltByteArray);
return GetHexadecimalString(saltByteArray);
}
#endregion
//////////////////////////////////////////////////////////////////////////////// Private
#region 16진수 문자열에서 바이트 배열 구하기 - GetByteArrayFromHexadecimalString(source)
/// <summary>
/// 16진수 문자열에서 바이트 배열 구하기
/// </summary>
/// <param name="source">소스 문자여</param>
/// <returns>바이트 배열</returns>
private static byte[] GetByteArrayFromHexadecimalString(string source)
{
source = source.Replace(" ", string.Empty);
int byteCount = source.Length / 2;
byte[] targetByteArray = new byte[byteCount];
for(int i = 0; i < byteCount; i++)
{
targetByteArray[i] = Convert.ToByte(source.Substring(2 * i, 2), 16);
}
return targetByteArray;
}
#endregion
#region 16진수 문자열 구하기 - GetHexadecimalString(sourceByteArray)
/// <summary>
/// 16진수 문자열 구하기
/// </summary>
/// <param name="sourceByteArray">소스 바이트 배열</param>
/// <returns>16진수 문자열</returns>
private static string GetHexadecimalString(byte[] sourceByteArray)
{
string target = string.Empty;
foreach(byte sourceByte in sourceByteArray)
{
target += " " + sourceByte.ToString("X2");
}
if(target.Length > 0)
{
target = target.Substring(1);
}
return target;
}
#endregion
#region 키/초기 값 만들기 - MakeKeyAndInitialValue(password, saltByteArray, keySizeBitCount, blockSizeBitCount, keyByteArray, initialValueByteArray)
/// <summary>
/// 키/초기 값 만들기
/// </summary>
/// <param name="password">패스워드</param>
/// <param name="saltByteArray">솔트 바이트 배열</param>
/// <param name="keySizeBitCount">키 크기 비트 카운트</param>
/// <param name="blockSizeBitCount">블럭 크기 비트 카운트</param>
/// <param name="keyByteArray">키 바이트 배열</param>
/// <param name="initialValueByteArray">초기 값 바이트 배열</param>
private static void MakeKeyAndInitialValue
(
string password,
byte[] saltByteArray,
int keySizeBitCount,
int blockSizeBitCount,
ref byte[] keyByteArray,
ref byte[] initialValueByteArray
)
{
Rfc2898DeriveBytes rfc2898DeriveBytes = new Rfc2898DeriveBytes(password, saltByteArray, 513);
keyByteArray = rfc2898DeriveBytes.GetBytes(keySizeBitCount / 8);
initialValueByteArray = rfc2898DeriveBytes.GetBytes(blockSizeBitCount / 8);
}
#endregion
#region 암호화/복호화하기 - EncryptOrDecrypt(password, sourceByteArray, salt, encrypt)
/// <summary>
/// 암호화/복호화하기
/// </summary>
/// <param name="password">패스워드</param>
/// <param name="sourceByteArray">소스 바이트 배열</param>
/// <param name="salt">솔트</param>
/// <param name="encrypt">암호화 여부</param>
/// <returns>암호화/복호화 바이트 배열</returns>
private static byte[] EncryptOrDecrypt(string password, byte[] sourceByteArray, string salt, bool encrypt)
{
AesCryptoServiceProvider provider = new AesCryptoServiceProvider();
int keySizeBitCount = 0;
for(int i = 1024; i >= 1; i--)
{
if(provider.ValidKeySize(i))
{
keySizeBitCount = i;
break;
}
}
int blockSizeBitCount = provider.BlockSize;
byte[] saltByteArray = GetByteArrayFromHexadecimalString(salt);
byte[] keyByteArray = null;
byte[] initialValueByteArray = null;
MakeKeyAndInitialValue(password, saltByteArray, keySizeBitCount, blockSizeBitCount, ref keyByteArray, ref initialValueByteArray);
ICryptoTransform cryptoTransform;
if(encrypt)
{
cryptoTransform = provider.CreateEncryptor(keyByteArray, initialValueByteArray);
}
else
{
cryptoTransform = provider.CreateDecryptor(keyByteArray, initialValueByteArray);
}
MemoryStream targetStream = new MemoryStream();
CryptoStream cryptoStream = new CryptoStream(targetStream, cryptoTransform, CryptoStreamMode.Write);
cryptoStream.Write(sourceByteArray, 0, sourceByteArray.Length);
try
{
cryptoStream.FlushFinalBlock();
}
catch(CryptographicException)
{
}
catch
{
throw;
}
byte[] targetByteArray = targetStream.ToArray();
try
{
cryptoStream.Close();
}
catch(CryptographicException)
{
}
catch
{
throw;
}
targetStream.Close();
return targetByteArray;
}
#endregion
#region 암호화하기 - Encrypt(source, password, salt)
/// <summary>
/// 암호화하기
/// </summary>
/// <param name="source">소스 문자열</param>
/// <param name="password">패스워드</param>
/// <param name="salt">솔트</param>
/// <returns>암호화 바이트 배열</returns>
private static byte[] Encrypt(string source, string password, string salt)
{
UTF8Encoding encoding = new UTF8Encoding();
byte[] sourceByteArray = encoding.GetBytes(source);
return EncryptOrDecrypt(password, sourceByteArray, salt, true);
}
#endregion
#region 복호화하기 - Decrypt(sourceByteArray, password, salt)
/// <summary>
/// 복호화하기
/// </summary>
/// <param name="sourceByteArray">소스 바이트 배열</param>
/// <param name="password">패스워드</param>
/// <param name="salt">솔트</param>
/// <returns>복호화 문자열</returns>
private static string Decrypt(byte[] sourceByteArray, string password, string salt)
{
byte[] targetByteArray = EncryptOrDecrypt(password, sourceByteArray, salt, false);
UTF8Encoding encoding = new UTF8Encoding();
return new string(encoding.GetChars(targetByteArray));
}
#endregion
}
}