[c#] 사용자 이름 / 암호 (로컬)를 안전하게 저장하는 방법은 무엇입니까?

먼저 로그인해야하는 Windows 응용 프로그램을 만들고 있습니다.
계정 세부 정보는 사용자 이름과 암호로 구성되며 로컬에 저장해야합니다.
보안 문제 일 뿐이므로 같은 컴퓨터를 사용하는 다른 사람들은 모든 사람의 개인 데이터를 볼 수 없습니다.
이 데이터를 저장하는 가장 안전한 방법은 무엇입니까?

데이터베이스를 사용하고 싶지 않아 리소스 파일로 몇 가지 시도했습니다.
그러나 나는 이것에 익숙하지 않기 때문에 내가 무엇을하고 있고 어디에서 해결책을 찾아야하는지 완전히 확신하지 못합니다.



답변

입력 한 사용자 이름과 암호를 확인 / 검증하려는 경우 Rfc2898DerivedBytes 클래스 (암호 기반 키 파생 함수 2 또는 PBKDF2라고도 함)를 사용하십시오. RFC2898DerivedBytes 결과에서 암호로 다시 이동할 수있는 실용적인 방법이 없기 때문에 Triple DES 또는 AES와 같은 암호화를 사용하는 것보다 더 안전합니다. 암호에서 결과로만 이동할 수 있습니다. 암호 문자열에서 암호화 키 및 IV를 가져올 때 암호의 SHA1 해시를 솔트로 사용해도 괜찮습니까?를 참조하십시오 . .Net 또는 String 에 대한 예제 및 토론 은 WinRT / Metro 용 암호 c # Metro 스타일암호화 / 복호화합니다 .

타사에 제공하는 등 재사용을 위해 암호를 저장하는 경우 Windows DPAPI (데이터 보호 API)를 사용하십시오 . 이는 운영 체제 생성 및 보호 키와 Triple DES 암호화 알고리즘을 사용하여 정보를 암호화하고 해독합니다. 즉, 애플리케이션은 암호화를 사용할 때 주요 관심사 인 암호화 키 생성 및 보호에 대해 걱정할 필요가 없습니다.

C #에서는 System.Security.Cryptography.ProtectedData 클래스를 사용합니다 . 예를 들어 데이터를 암호화하려면 ProtectedData.Protect()다음을 사용하십시오 .

// Data to protect. Convert a string to a byte[] using Encoding.UTF8.GetBytes().
byte[] plaintext;

// Generate additional entropy (will be used as the Initialization vector)
byte[] entropy = new byte[20];
using(RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider())
{
    rng.GetBytes(entropy);
}

byte[] ciphertext = ProtectedData.Protect(plaintext, entropy,
    DataProtectionScope.CurrentUser);

현재 사용자 만 읽을 수 있도록 권한이 설정된 파일 또는 레지스트리 키와 같이 엔트로피 및 암호문을 안전하게 저장합니다. 원본 데이터에 액세스하려면 다음을 사용하십시오 ProtectedData.Unprotect().

byte[] plaintext= ProtectedData.Unprotect(ciphertext, entropy,
    DataProtectionScope.CurrentUser);

추가 보안 고려 사항이 있습니다. 예를 들어 비밀번호와 같은 비밀을 string. 문자열은 변경할 수 없습니다. 메모리에서 알림을받을 수 없기 때문에 응용 프로그램의 메모리 나 메모리 덤프를 보는 사람이 암호를 볼 수 있습니다. 대신 SecureString 또는 byte []를 사용하고 암호가 더 이상 필요하지 않으면 즉시 폐기하거나 0으로 설정해야합니다.


답변

나는 이전에 이것을 사용했으며 자격 증명이 지속되고 가장 안전한 방법으로

  1. ConfigurationManager클래스를 사용하여 앱 구성 파일에 쓸 수 있습니다.
  2. SecureString클래스를 사용하여 암호 보안
  3. 그런 다음 Cryptography네임 스페이스의 도구를 사용하여 암호화합니다 .

이 링크는 내가 바라는 큰 도움이 될 것입니다 : 여기를 클릭하십시오


답변

DPAPI는이 목적을위한 것입니다. DPAPI를 사용하여 사용자가 처음 입력 할 때 암호를 암호화하고 안전한 위치에 저장합니다 (사용자 레지스트리, 사용자 응용 프로그램 데이터 디렉터리 등이 선택 사항 임). 앱이 시작될 때마다 위치를 확인하여 키가 있는지, DPAPI를 사용하여 암호를 해독하고 액세스를 허용하는지, 그렇지 않으면 거부합니다.


답변

이것은 Windows에서만 작동하므로 dotnet core 크로스 플랫폼을 사용하려는 경우 다른 곳을 찾아야합니다. 참조 https://github.com/dotnet/corefx/blob/master/Documentation/architecture/cross-platform-cryptography.md를


답변

문자열을 읽을 수있는 문자열로 암호화하고 해독하고 싶었습니다.

다음은 .NET Core의 답변을 기반으로 한 C # Visual Studio 2019 WinForms의 매우 간단한 간단한 예제입니다 @Pradip.

프로젝트> 속성> 설정> usernamepassword설정 만들기를 마우스 오른쪽 버튼으로 클릭 합니다.

여기에 이미지 설명 입력

이제 방금 만든 설정을 활용할 수 있습니다. 저는 여기에 저장 username하고 password있지만 암호화 password는 존경 값 필드가에 있어요에 user.config파일.

user.config파일 에있는 암호화 된 문자열의 예 .

<?xml version="1.0" encoding="utf-8"?>
<configuration>
    <userSettings>
        <secure_password_store.Properties.Settings>
            <setting name="username" serializeAs="String">
                <value>admin</value>
            </setting>
            <setting name="password" serializeAs="String">
                <value>AQAAANCMnd8BFdERjHoAwE/Cl+sBAAAAQpgaPYIUq064U3o6xXkQOQAAAAACAAAAAAAQZgAAAAEAACAAAABlQQ8OcONYBr9qUhH7NeKF8bZB6uCJa5uKhk97NdH93AAAAAAOgAAAAAIAACAAAAC7yQicDYV5DiNp0fHXVEDZ7IhOXOrsRUbcY0ziYYTlKSAAAACVDQ+ICHWooDDaUywJeUOV9sRg5c8q6/vizdq8WtPVbkAAAADciZskoSw3g6N9EpX/8FOv+FeExZFxsm03i8vYdDHUVmJvX33K03rqiYF2qzpYCaldQnRxFH9wH2ZEHeSRPeiG</value>
            </setting>
        </secure_password_store.Properties.Settings>
    </userSettings>
</configuration>

여기에 이미지 설명 입력

전체 코드

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Security;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace secure_password_store
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void Exit_Click(object sender, EventArgs e)
        {
            Application.Exit();
        }

        private void Login_Click(object sender, EventArgs e)
        {
            if (checkBox1.Checked == true)
            {
                Properties.Settings.Default.username = textBox1.Text;
                Properties.Settings.Default.password = EncryptString(ToSecureString(textBox2.Text));
                Properties.Settings.Default.Save();
            }
            else if (checkBox1.Checked == false)
            {
                Properties.Settings.Default.username = "";
                Properties.Settings.Default.password = "";
                Properties.Settings.Default.Save();
            }
            MessageBox.Show("{\"data\": \"some data\"}","Login Message Alert",MessageBoxButtons.OK, MessageBoxIcon.Information);
        }
        private void DecryptString_Click(object sender, EventArgs e)
        {
            SecureString password = DecryptString(Properties.Settings.Default.password);
            string readable = ToInsecureString(password);
            textBox4.AppendText(readable + Environment.NewLine);
        }
        private void Form_Load(object sender, EventArgs e)
        {
            //textBox1.Text = "UserName";
            //textBox2.Text = "Password";
            if (Properties.Settings.Default.username != string.Empty)
            {
                textBox1.Text = Properties.Settings.Default.username;
                checkBox1.Checked = true;
                SecureString password = DecryptString(Properties.Settings.Default.password);
                string readable = ToInsecureString(password);
                textBox2.Text = readable;
            }
            groupBox1.Select();
        }


        static byte[] entropy = Encoding.Unicode.GetBytes("SaLtY bOy 6970 ePiC");

        public static string EncryptString(SecureString input)
        {
            byte[] encryptedData = ProtectedData.Protect(Encoding.Unicode.GetBytes(ToInsecureString(input)),entropy,DataProtectionScope.CurrentUser);
            return Convert.ToBase64String(encryptedData);
        }

        public static SecureString DecryptString(string encryptedData)
        {
            try
            {
                byte[] decryptedData = ProtectedData.Unprotect(Convert.FromBase64String(encryptedData),entropy,DataProtectionScope.CurrentUser);
                return ToSecureString(Encoding.Unicode.GetString(decryptedData));
            }
            catch
            {
                return new SecureString();
            }
        }

        public static SecureString ToSecureString(string input)
        {
            SecureString secure = new SecureString();
            foreach (char c in input)
            {
                secure.AppendChar(c);
            }
            secure.MakeReadOnly();
            return secure;
        }

        public static string ToInsecureString(SecureString input)
        {
            string returnValue = string.Empty;
            IntPtr ptr = System.Runtime.InteropServices.Marshal.SecureStringToBSTR(input);
            try
            {
                returnValue = System.Runtime.InteropServices.Marshal.PtrToStringBSTR(ptr);
            }
            finally
            {
                System.Runtime.InteropServices.Marshal.ZeroFreeBSTR(ptr);
            }
            return returnValue;
        }

        private void EncryptString_Click(object sender, EventArgs e)
        {
            Properties.Settings.Default.password = EncryptString(ToSecureString(textBox2.Text));
            textBox3.AppendText(Properties.Settings.Default.password.ToString() + Environment.NewLine);
        }
    }
}


답변