C#에서 암호화 사용하기
작년에 친구가 컴퓨터 전원을 꺼버려서 내 파일에 대한 복수의 의미로 랜섬웨어를 만들었던 기억이 나서 C#으로 암호화 하는 법을 적어볼 것이다.
암호화는 ASP.NET 프로젝트를 진행하면서 비밀번호 저장 등 민감한 정보를 다룰 때 자주 쓰이니까 라이브러리로 만들어두고 편하게 쓰자.
필요한 것
- C# 개발환경 하나면 충분하다.
암호화 하기 전 만들어야 하는 것
우리가 사용할 알고리즘이 AES이라는 알고리즘인데, AES 알고리즘은 암호화 작업을 수행할 때 키와 초기화 벡터라는 값을 요구한다. 그래서 키와 초기화 벡터를 생성하는 메소드를 미리 만들 것이다.
키 생성 메소드
public static Rfc2898DeriveBytes CreateKey(string password) {
byte[] keyBytes = Encoding.UTF8.GetBytes(password); //키값 생성
byte[] saltBytes = SHA512.Create().ComputeHash(keyBytes); //솔트값(원본 키값을 알기 어렵게 하는 값)
Rfc2898DeriveBytes result = new Rfc2898DeriveBytes(keyBytes, saltBytes, 100000); //키값에 솔트값을 사용해 새로운 키 생성, 마지막에 들어가는 수는 해시 생성의 반복 횟수이다.
return result; //키값 반환
}
초기화 벡터 생성 메소드
public static Rfc2898DeriveBytes CreateVector(string vector) {
byte[] vectorBytes = Encoding.UTF8.GetBytes(vector); //벡터 생성
byte[] saltBytes = SHA512.Create().ComputeHash(vectorBytes); //솔트값(원본 벡터를 알기 어렵게 하는 값)
Rfc2898DeriveBytes result = new Rfc2898DeriveBytes(vectorBytes, saltBytes, 100000); //벡터에 솔트값을 사용해 새로운 키 생성, 마지막에 들어가는 수는 해시 생성의 반복 횟수이다.
return result; //벡터 반환
}
암호화 메소드 생성
벡터를 생성할때 사용한 문자열은 여기에서 가져왔다. 유추하기 어려운 문자열을 쓰고 싶을때 유용한 사이트다.
이 예제에서는 AES-256으로 암호화를 진행한다.
public static byte[] Encrypt(byte[] origin, string password) {
RijndaelManaged aes = new RijndaelManaged(); //AES 알고리즘
Rfc2898DeriveBytes key = CreateKey(password); //키값 생성
Rfc2898DeriveBytes vector = CreateVector("ZaWmAcu1C2fbgJa4cPuZrT6MhuWmx6GE"); //벡터 생성
aes.BlockSize = 128; //AES의 블록 크기는 128 고정이다.
aes.KeySize = 256; //AES의 키 크기는 128, 192, 256을 지원한다.
aes.Mode = CipherMode.CBC;
aes.Padding = PaddingMode.PKCS7;
aes.Key = key.GetBytes(32); //AES-256을 사용하므로 키값의 길이는 32여야 한다.
aes.IV = vector.GetBytes(16); //초기화 벡터는 언제나 길이가 16이어야 한다.
//키값과 초기화 벡터를 기반으로 암호화 작업을 하는 클래스 변수를 생성
ICryptoTransform encryptor = aes.CreateEncryptor(aes.Key, aes.IV);
//using블록으로 변수를 사용하면 블록에서 나올때 자동으로 변수가 가비지컬렉팅 된다.
using(MemoryStream ms = new MemoryStream()) //결과를 담을 스트림
{
//encryptor 변수에서 암호화된 데이터를 결과에 쓰는 스트림
using(CryptoStream cs = new CryptoStream(ms, encryptor, CryptoStreamMode.Write))
{
cs.Write(origin, 0, origin.Length);
}
return ms.ToArray(); //암호화된 바이트 배열 반환
}
}
복호화 메소드 생성
복호화할때 주의할 점은 벡터값이 암호화할 때랑 다르면 반드시 실패한다는 점이다. 위에서 사용한 원본 벡터 문자열을 그대로 사용해야 복호화를 할 수 있다.
마찬가지로 AES-256으로 복호화를 진행한다.
public static byte[] Decrypt(byte[] origin, string password) {
RijndaelManaged aes = new RijndaelManaged(); //AES 알고리즘
Rfc2898DeriveBytes key = CreateKey(password); //키값 생성
Rfc2898DeriveBytes vector = CreateVector("ZaWmAcu1C2fbgJa4cPuZrT6MhuWmx6GE"); //벡터 생성
aes.BlockSize = 128; //AES의 블록 크기는 128 고정이다.
aes.KeySize = 256; //AES의 키 크기는 128, 192, 256을 지원한다.
aes.Mode = CipherMode.CBC;
aes.Padding = PaddingMode.PKCS7;
aes.Key = key.GetBytes(32); //AES-256을 사용하므로 키값의 길이는 32여야 한다.
aes.IV = vector.GetBytes(16); //초기화 벡터는 언제나 길이가 16이어야 한다.
//키값과 초기화 벡터를 기반으로 복호화 작업을 하는 클래스 변수를 생성
ICryptoTransform decryptor = aes.CreateDecryptor(aes.Key, aes.IV);
//using블록으로 변수를 사용하면 블록에서 나올때 자동으로 변수가 가비지컬렉팅 된다.
using(MemoryStream ms = new MemoryStream()) //결과를 담을 스트림
{
//encryptor 변수에서 복호화된 데이터를 결과에 쓰는 스트림
using(CryptoStream cs = new CryptoStream(ms, decryptor, CryptoStreamMode.Write))
{
cs.Write(origin, 0, origin.Length);
}
return ms.ToArray(); //복호화된 바이트 배열 반환
}
}
사실 복호화 예제랑 암호화 예제에서 변화한 것은 encryptor를 생성할때 사용한 메소드 이름의 단 두글자밖에 없다. 알고리즘의 기반이 같고 배열에 변화한 값을 쓰는 건 같기 때문이다.
댓글남기기