본문 바로가기
C#

c# 데이터 암복호화 막 하지 말자.

by ZEC 2021. 2. 6.

오늘은 회사에서 프로젝트를 진행하면서 사용했던 데이터 암호화에 대해 공부해보려고 합니다.

많은 블로그 글에서 데이터 암호화에 대한 주제를 가지고 많이 다루고 있습니다.

하지만 대부분의 글들은 매우 위험한 방법으로 암호화를 하고 있는데요?

이유는 암복호화 시 사용하는 키(KEY)를 프로젝트 소스에 담고 있습니다.

이는 프로젝트 소스를 난독화 하지 않는 이상 누구나 디컴파일을 통해 프로젝트를 까 보면

키를 알 수 있고 해독을 할 수 있습니다.

 

물론 개인이 간단하게 사용할 프로젝트나 공부에서는 해당 글들을 보고 암복호화를 진행하셔도 좋습니다.

하지만 실제로 서비스하는 프로젝트라면 절대로 있어서는 안 될 소스라는 거죠.

 

나더러 어쩌란 말이냐?


그럼 어떻게 키(KEY)를 프로젝트 내에 가지고 있지 않으면서 암복호화를 할 수 있을까요?

암복호화를 진행할 때 본인의 서버에서 키를 불러오는 방법이 있겠고요

(이 방법은 서버도 개발을 해야 하니 매우 귀찮아요 우린 편안함을 추구합시다. ㅋㅋㅋㅋㅋ)

제가 사용했던 방법은 서버를 이용하지 않고 윈도우(Windows) 자체에서 지원하고 있는 DPAIP를 이용하는 방법입니다.

해당 방법은 윈도우(Windows) 플랫폼에서만 지원하고 있으니 이외의 플랫폼에서는 사용할 수 없습니다.

이점은 꼭 참고를 해주시고요 우선 DPAIP에 대한 링크를 아래에 남겨드릴게요.

데이터 암복호화 메서드를 제공하는 ProtectedData 클래스를 이용할 거예요!

 

 

ProtectedData 클래스 (System.Security.Cryptography)

데이터를 암호화하거나 해독하는 메서드를 제공합니다.Provides methods for encrypting and decrypting data. 이 클래스는 상속될 수 없습니다.This class cannot be inherited.

docs.microsoft.com

예제


데이터 암복호화는 주로 패스워드나 계좌번호 카드번호 등 민감한 부분에 사용을 하는데

저는 비밀번호를 암호화하는 예를 들어해 볼게요!

 

암복호화를 해줄 ProtectedDataTest 클래스는 다음과 같습니다.

using을 보시면 using System.Security.Cryptography; 따로 추가 라이브러리가 필요하지 않습니다.

 

using System;
using System.Security.Cryptography;
using System.Text;

namespace ConsoleApp1
{
    public class ProtectedDataTest
    {
        public static string Protect(string origin)
        {
            var PasswordProtect = Convert.ToBase64String(ProtectedData.Protect(Encoding.UTF8.GetBytes(origin), null, DataProtectionScope.CurrentUser));
            return PasswordProtect;
        }

        public static string Unprotect(string origin)
        {
            var PasswordUnprotect = Encoding.UTF8.GetString(ProtectedData.Unprotect(Convert.FromBase64String(origin), null, DataProtectionScope.CurrentUser));
            return PasswordUnprotect;
        }
    }
}

 

비밀번호를 암호화해주는 Protect

비밀번호를 복호화해주는 Unprotect

 

그럼 사용을 해볼까요?

비밀번호는 제가 임의로 TestPassword1234라고 지정을 했는데요

결과는 다음과 같습니다. 성공적이죠?

 

using System;

namespace ConsoleApp1
{
    class Program
    {
        static void Main(string[] args)
        {
            var Password = "TestPassword1234";
            Console.WriteLine($"비밀번호 암호화 하기전 : {Password}");
            
            Password = ProtectedDataTest.Protect(Password);
            Console.WriteLine($"비밀번호 암호화 : {Password}");

            Password = ProtectedDataTest.Unprotect(Password);
            Console.WriteLine($"비밀번호 복호화 : {Password}");
        }
    }
}

########## 결 과 ##########

비밀번호 암호화 하기전 : TestPassword1234

비밀번호 암호화 : AQAAANCMnd8BFdERjHoAwE/Cl+sBAAAA1u1MglqslECAi9YraHG6aQAAAAACAAAAAAAQZgAAAAEAACAAAAAksPRmzlwbG5RgWnxb8K9kjhaT96Avnxr3uf7Otoy/ZgAAAAAOgAAAAAIAACAAAAARzNc447prORHeOvWpCc7XtaqzDsR/01mFS7P/3mvkXSAAAABulPdXfpGSHR/kg708Fx5+uV69lW7oj7T3iiu40/Qa6EAAAAC6v4FJDCXcZPncEBSncTCyOdXOND2s3wO7iUgHwCmPsvR7qZdwKhscfYTXQtBPtVu0/Qi0ZzBCs8trx/gKAWRZ

비밀번호 복호화 : TestPassword1234

 

문제점


하지만 이 코드는 문제를 발생시킬 수 있습니다.

윈도우(Windows)를 정상적으로 이용하는 사용자라면 별 문제가 없겠지만?

사실 윈도우(Windows)는 정품을 사용하지 않는 사람이 적어도 우리나라엔 엄청 많습니다.

또 윈도우(Windows)를 사용하는 방법을 모르고 설정을 바꾸는 경우도 있는데요 바로 이런 부분에서

문제가 발생했었고 테스트와 오랜 삽질을 통해 문제를 해결했습니다.

 

제가 테스트를 하면서 발견했던 에러는 다음과 같습니다.

 

System.Security.Cryptography.CryptographicException: 키를 지정된 상태에서 사용하기에 부적합합니다.

위에 올려드렸던 MSDN 링크의 글을 보시면 아시겠지만 해당 글에서도 위와 같은 오류가 표시될 수 있다는

얘기를 하고 있는데요 내용은 다음과 같습니다.

 

가장을 수행 하는 동안 이러한 메서드를 사용 하는 경우 "지정 된 상태에서 키를 사용할 수 없습니다." 라는 오류가 표시 될 수 있습니다. 이는 DPAPI가 사용자 프로필에 키 데이터를 저장 하기 때문에 발생 합니다. 프로필이 로드 되지 않은 경우 DPAPI에서 암호 해독을 수행할 수 없습니다. 이 오류를 방지 하려면 메서드를 호출 하기 전에 가장 하려는 사용자의 프로필을 로드 합니다. 가장에 DPAPI를 사용 하면 심각한 문제가 발생할 수 있으며 신중 하 게 디자인을 선택 해야 합니다.

위 내용을 보면 해당 메서드를 사용하기 전에 사용자 프로필을 로드하라는 내용인데요 이게 사실은

말이 안 됩니다. 사용자 프로필을 로드하는 것은 관리자 권한이 필요한 일인데요? 이미 관리자 권한을 가지고 사용하는

프로젝트의 경우에는 사용자 프로필을 미리 로드해서 사용할 수 있겠지만 제가 진행하는 프로젝트의 경우 관리자

권한이 없어야 하는 프로젝트이기에 위의 문제는 정말 심각했습니다. 다른 블로그들도 많이 참고를 했지만

답이 없었습니다 사용자 프로필을 로드시켜라 라는 말밖에는 요..

 

오랜 삽질 끝에 해결을 할 수 있었는데요? 위에 전제조건으로 깔았던

윈도우(Windows)를 정상적이게 이용하지 않는 평범한 이용자들이 있기 때문입니다.

(비하 발언 아니에요! 저도 평범한 이용자 입니다. ㅜㅜ)

 

제가 암복호화를 하는 코드를 보시면

보호 범위를 DataProtectionScope.CurrentUser로 지정을 했었습니다.

MSDN 설명을 봐도 CurrentUser로 사용해야 안전하다. 또 대부분은 CurrentUser 를사용한다

라는 내용만 보고 진행을 했던 것인데 이게 문제가 될 줄을 몰랐습니다.

 

  • CurrentUser - 보호된 데이터가 현재 사용자에 연결되어 있습니다. 현재 사용자 컨텍스트에서 실행되는 스레드에서만 데이터를 보호 해제할 수 있습니다.
  • LocalMachine - 보호된 데이터가 컴퓨터 컨텍스트에 연결되어 있습니다. 컴퓨터에서 실행되는 모든 프로세스에서 데이터를 보호 해제할 수 있습니다. 일반적으로 이 열거형 값은 신뢰되지 않는 사용자의 액세스가 허용되지 않는 서버에서 실행되는 서버 관련 애플리케이션에 사용됩니다.

제가 발견했던 사용자의 pc는 윈도우(Windows) 계정 이름과 사용자 폴더 이름이 상이한 경우가 엄청 많았습니다

이게 왜 틀어졌는지는 이해가 안 되지만 이런 경우에 대부분 위와 같은 문제점이 발생하는 걸 파악할 수 있었습니다.

 

해결


보호 범위를 DataProtectionScope.LocalMachine으로 사용하는 걸 추천드리며 글을 마치겠습니다.

 

앗! 추가로 이미 DataProtectionScope.CurrentUser로 보호 범위를 정하셔서 사용하셨다면 따로 마이그레이션

작업은 하지 않으셔도 됩니다.(테스트를 통해 확인했습니다.) DataProtectionScope.LocalMachine 변경 이후에 비밀번호를 재설정하는 작업만 진행하시면 됩니다.

댓글