C#에서 AES256으로 암호화 하기

예전부터 한번 해봐야지 하고 생각만 하다가 안했던 암호화처리

요즘은 나름 보안에 관심을 가져야 겠다 라는 생각에 데이터 네트워크로 송수신할때 암호화는 적용해야 하지 않을까 라는 생각에 간단한 예제 해봄.

암호화에 대한 기초 지식은 https://www.slideshare.net/ssuser800974/ss-76664853 를 참고함

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;
 
namespace AESTest2
{
    class AESEncrypt
    {
        private SHA256Managed sha256Managed = new SHA256Managed();
        private RijndaelManaged aes = new RijndaelManaged();
 
        public AESEncrypt()
        {
            aes.KeySize = 256;
            aes.BlockSize = 128;
            aes.Mode = CipherMode.CBC;
            aes.Padding = PaddingMode.PKCS7;
        }
 
 
        //AES_256 암호화
        public byte[] AESEncrypt256(byte[] encryptData, String password)
        {
            // Salt는 비밀번호의 길이를 SHA256 해쉬값으로 한다.
            var salt = sha256Managed.ComputeHash(Encoding.UTF8.GetBytes(password.Length.ToString()));
            Console.WriteLine("Salt(Base64) : " + Convert.ToBase64String(salt));
 
 
            //PBKDF2(Password-Based Key Derivation Function)
            //반복은 65535번
            var PBKDF2Key   = new Rfc2898DeriveBytes(password, salt, 65535, HashAlgorithmName.SHA256);
            var secretKey   = PBKDF2Key.GetBytes(aes.KeySize / 8);
            var iv          = PBKDF2Key.GetBytes(aes.BlockSize / 8);
 
            Console.WriteLine("SecretKey(Base64) : " + Convert.ToBase64String(secretKey));
            Console.WriteLine("IV(Base64) : " + Convert.ToBase64String(iv));
 
            byte[] xBuff = null;
            using (var ms = new MemoryStream())
            {
                using (var cs = new CryptoStream(ms, aes.CreateEncryptor(secretKey, iv), CryptoStreamMode.Write))
                {
                    cs.Write(encryptData, 0, encryptData.Length);
                }
                xBuff = ms.ToArray();
            }
            return xBuff;
        }
 
        //AES_256 복호화
        public byte[] AESDecrypt256(byte[] decryptData, String password)
        {
            // Salt는 비밀번호의 길이를 SHA256 해쉬값으로 한다.
            var salt = sha256Managed.ComputeHash(Encoding.UTF8.GetBytes(password.Length.ToString()));
 
            //PBKDF2(Password-Based Key Derivation Function)
            //반복은 65535번
            var PBKDF2Key = new Rfc2898DeriveBytes(password, salt, 65535, HashAlgorithmName.SHA256);
            var secretKey = PBKDF2Key.GetBytes(aes.KeySize / 8);
            var iv = PBKDF2Key.GetBytes(aes.BlockSize / 8);
 
            byte[] xBuff = null;
            using (var ms = new MemoryStream())
            {
                using (var cs = new CryptoStream(ms, aes.CreateDecryptor(secretKey, iv), CryptoStreamMode.Write))
                {
                    cs.Write(decryptData, 0, decryptData.Length);
                }
                xBuff = ms.ToArray();
            }
            return xBuff;
        }
 
    }
 
    class Program
    {
        static void Main(string[] args)
        {
            AESEncrypt aes = new AESEncrypt();
            var planeText = "https://linsoo.co.kr";
            var password = "비밀번호";
 
            Console.WriteLine("오리지널 문장 : "+planeText);
            Console.WriteLine("대칭키로 쓰일 키 : " + password);
            Console.WriteLine("");
 
            //스트링을 byte배열로 변환
            var byteArray = Encoding.UTF8.GetBytes(planeText);
 
            //AES256으로 인크립트
            byte[] encryptedArray = aes.AESEncrypt256(byteArray, password);
            Console.WriteLine("");
            Console.WriteLine("인크립트 된 문자열 : " + Encoding.UTF8.GetString(encryptedArray));
 
            //디크립트(AES256)
            byte[] decryptedArray = aes.AESDecrypt256(encryptedArray, password);
            var decryptedString = Encoding.UTF8.GetString(decryptedArray);
            Console.WriteLine("디크립트 된 문자열 : " + decryptedString);
            Console.WriteLine("");
        }
    }
}

다른 예제들 보면 대부분 IV 값을 0으로 넣는데 0이면 큰 의미는 없을거 같아서 검색해보니 패스워드 키값을 가지고 이래저래 섞어서 쓰길래 나도 적용함.
반복은 많이 할수록 느려지지만 그만큼 풀기가 힘들어진다고 함.

암튼 그래서 위와 같은 방식으로 작업 완료.
C#에서는 기본적으로 제공되는 함수가 많아서 편리하고 간단하게 작업되서 좋았음.
전문가들 추천으로는 암호화는 절대로 독자적인 방법으로 만들지 말고 남들이 많이 쓰는 안전하다고 입증된 알고리즘을 입증된 라이브러리 써서 적용하라고 함.

댓글 8개

  1. 아. 전달을 잘못했군요.
    해당 코드를 라이센스 파일 만들고 파일을 읽어서 체크하는 로직으로 쓰려고 하였습니다.
    인크립트 함수를 사용한 리턴배열을 Encoding.UTF8.GetString(encryptedArray)) 함수내용으로 나온 문자를
    라이센스 파일로 만들었습니다.

    이 파일을 read해서 Encoding.UTF8.GetBytes 함수로 변환한 바이트배열을 디크립트 함수에 넣었을때 에러가 나서요..

    혹시 파일을 만들고 읽어서 문제인가 싶어 Getstring으로 나온 값을 변수에 담아서 Getbyte로 변경하여 디크립트 하였지만 동일하게 에러가 나는군요.

    바이트 배열을 바로 파일로 쓰고 읽어서 사용하는게 맞는지 궁금하네요.

    1. 비슷한 문제를 c#쪽이 아니라 코틀린으로 작업하다 겪었는데 인코딩 함수가 GetBytes 하면 입력할때 넣은 바이트 배열이랑 길이가 다르거나 끝부분에 00이 들어가 있다던지 차이가 있을수 있습니다.
      저는 그거 때문에 에러가 발생했었습니다. 한번 확인해 보세요.

  2. 디크립션에 인크립트된 문자열을 넣고 Encoding.UTF8.GetBytes 디크립션을 하면 안되는데 혹시 인코딩 그런거 문제일까요?

    1. 디크립트 함수에 인크립트된 값을 넣고 나온 결과값을 UTF8로 인코딩 했다는건가요? 무슨말인지 잘 모르겠습니다.

  3. 심각도 코드 설명 프로젝트 파일 줄 비표시 오류(Suppression) 상태
    오류 CS1729 'Rfc2898DeriveBytes'에는 인수를 4개 사용하는 생성자가 포함되어 있지 않습니다. ConsoleApp1 C:\Users\82105\Documents\Visual Studio 2017\Projects\ConsoleApp1\ConsoleApp1\AES256.cs 36 활성

    이렇게 나오는데 이거 왜이럴까요 …. ? ㅠㅠ

    1. 타깃 프레임웍 버전이 4.72 미만이라 그렇습니다.

      https://docs.microsoft.com/ko-kr/dotnet/api/system.security.cryptography.rfc2898derivebytes.-ctor?view=netframework-4.7.2#System_Security_Cryptography_Rfc2898DeriveBytes__ctor_System_String_System_Byte___System_Int32_System_Security_Cryptography_HashAlgorithmName_

      4.71까지는 4개 쓰는 생성자가 없었네요.
      타깃 프레임웍 버전을 올리거나 아니면 아랫버전에 맞게 수정해 쓰셔야 할듯 싶습니다.

댓글 달기

이메일 주소는 공개되지 않습니다.