ProtectedData.Protect периодический сбой

Я пишу процедуру шифрования пароля. Я написал приложение ниже, чтобы проиллюстрировать свою проблему. Примерно в 20% случаев этот код работает должным образом. В остальное время расшифровка выдает криптографическое исключение - «Данные недействительны».

Я считаю, что проблема в части шифрования, потому что часть дешифрования работает каждый раз одинаково. То есть, если процедура шифрования дает значение, которое процедура дешифрования может расшифровать, она всегда может его расшифровать. Но если подпрограмма шифрования дает значение, которое подавляет подпрограмму расшифровки, она всегда подавляется. Таким образом, процедура дешифрования согласована; процедура шифрования - нет.

Я подозреваю, что я неправильно использую кодировку Unicode, но я пробовал другие с тем же результатом.

Что я делаю не так?

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

namespace DataProtectionTest
{
    public partial class Form1 : Form
    {
        private static readonly byte[] entropy = { 1, 2, 3, 4, 1, 2, 3, 4 };
        private string password;
        public Form1()
        {
            InitializeComponent();
        }

        private void btnEncryptIt_Click(object sender, EventArgs e)
        {
            Byte[] pw = Encoding.Unicode.GetBytes(textBox1.Text);
            Byte[] encryptedPw = ProtectedData.Protect(pw, entropy, DataProtectionScope.LocalMachine);
            password = Encoding.Unicode.GetString(encryptedPw);     
        }

        private void btnDecryptIt_Click(object sender, EventArgs e)
        {
            Byte[] pwBytes = Encoding.Unicode.GetBytes(password);
            try
            {
                Byte[] decryptedPw = ProtectedData.Unprotect(pwBytes, entropy, DataProtectionScope.LocalMachine);
                string pw = Encoding.Unicode.GetString(decryptedPw);
                textBox2.Text = pw;
            }
            catch (CryptographicException ce)
            {
                textBox2.Text = ce.Message;
            }
        }
    }
}
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
3
0
4 610
6
Перейти к ответу Данный вопрос помечен как решенный

Ответы 6

Я сильно подозреваю, что проблема связана с вызовом Encoding.Unicode.GetString. Вам необходимо убедиться, что данные, переданные вызову Unprotect, имеют значение точно, такое же, как данные, возвращенные вызовом Protect. Если вы кодируете двоичные данные как текст Unicode в качестве промежуточного шага, вы не можете этого гарантировать. В любом случае, зачем вам этот шаг - почему бы просто не сохранить byte []?

Я конвертирую обратно в строку, потому что мне нужно сохранить ее в базе данных как varchar.

Eric 29.10.2008 17:04

Лучшее решение - преобразовать массив байтов в строку с основанием 64.

Вы также можете использовать Latin-1 или ISO-8859-1 или кодовую страницу 28591 для этого сценария, поскольку он отображает значения в диапазоне 0-255 без изменений. Следующие элементы взаимозаменяемы:

Encoding.GetEncoding(28591)
Encoding.GetEncoding("Latin1")
Encoding.GetEncoding("iso-8859-1")

С этой кодировкой вы всегда сможете без потерь преобразовать byte [] -> string -> byte [].

См. эта почта для примера, который иллюстрирует использование этой кодировки.

Ответ принят как подходящий

По совету коллеги я выбрал Convert.ToBase64String. Работает хорошо. Исправленная программа ниже.

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

namespace DataProtectionTest
{
    public partial class Form1 : Form
    {
        private static readonly byte[] entropy = { 1, 2, 3, 4, 1, 2, 3, 4 };
        private string password;
        public Form1()
        {
            InitializeComponent();
        }

        private void btnEncryptIt_Click(object sender, EventArgs e)
        {
            Byte[] pw = Encoding.Unicode.GetBytes(textBox1.Text);
            Byte[] encryptedPw = ProtectedData.Protect(pw, entropy, DataProtectionScope.LocalMachine);
            //password = Encoding.Unicode.GetString(encryptedPw);       
            password = Convert.ToBase64String(encryptedPw);
        }

        private void btnDecryptIt_Click(object sender, EventArgs e)
        {
            //Byte[] pwBytes = Encoding.Unicode.GetBytes(password);
            Byte[] pwBytes = Convert.FromBase64String(password);
            try
            {
                Byte[] decryptedPw = ProtectedData.Unprotect(pwBytes, entropy, DataProtectionScope.LocalMachine);
                string pw = Encoding.Unicode.GetString(decryptedPw);
                textBox2.Text = pw;
            }
            catch (CryptographicException ce)
            {
                textBox2.Text = ce.Message;
            }
        }
    }
}

В этом коде все еще есть ошибка ... см. Мой ответ ниже.

Dave Black 30.12.2011 23:10

Преобразование его в строку Base64 и обратно решило мою проблему. Раньше я преобразовывал строку в Unicode и обратно. Действительно полезно. Спасибо

rideintothesun 12.08.2015 12:47

Проблема заключается в преобразовании в Unicode и завершении метода шифрования, Encoding.Unicode.GetString работает только в том случае, если байты, которые вы передаете ему, образуют допустимую строку UTF-16.

Я подозреваю, что иногда результат ProtectedData.Protect не является допустимой строкой UTF-16, поэтому Encoding.Unicode.GetString отбрасывает байты, которые не имеют смысла из возвращаемой строки, в результате чего строка не может быть преобразована обратно в зашифрованную данные.

Вы никогда не должны использовать какие-либо классы System.Text.Encoding для зашифрованного текста. У вас воля периодически возникают ошибки. Вы должны использовать кодировку Base64 и методы класса System.Convert.

  1. Чтобы получить зашифрованный string из зашифрованного byte[], вы должны использовать:

    Convert.ToBase64String(byte[] bytes)
    
  2. Чтобы получить необработанный byte[] из string для шифрования, вы должны использовать:

    Convert.FromBase64String(string data)
    

За дополнительной информацией обращайтесь к Сообщение гуру безопасности MS Шона Фаннинга.

Этот класс должен помочь:

public static class StringEncryptor
{
    private static readonly byte[] key = { 0x45, 0x4E, 0x3A, 0x8C, 0x89, 0x70, 0x37, 0x99, 0x58, 0x31, 0x24, 0x98, 0x3A, 0x87, 0x9B, 0x34 };

    public static string EncryptString(this string sourceString)
    {
        if (string.IsNullOrEmpty(sourceString))
        {
            return string.Empty;
        }

        var base64String = Base64Encode(sourceString);
        var protectedBytes = ProtectedData.Protect(Convert.FromBase64String(base64String), key, DataProtectionScope.CurrentUser);
        return Convert.ToBase64String(protectedBytes);
    }

    public static string DecryptString(this string sourceString)
    {
        if (string.IsNullOrEmpty(sourceString))
        {
            return string.Empty;
        }

        var unprotectedBytes = ProtectedData.Unprotect(Convert.FromBase64String(sourceString), key, DataProtectionScope.CurrentUser);
        var base64String = Convert.ToBase64String(unprotectedBytes);
        return Base64Decode(base64String);
    }

    private static string Base64Encode(string plainText)
    {
        var plainTextBytes = Encoding.UTF8.GetBytes(plainText);
        return Convert.ToBase64String(plainTextBytes);
    }

    private static string Base64Decode(string base64EncodedData)
    {
        var base64EncodedBytes = Convert.FromBase64String(base64EncodedData);
        return Encoding.UTF8.GetString(base64EncodedBytes);
    }
}

Другие вопросы по теме