System.Security.Cryptography.CryptographicException: «Неверный параметр» / Aes 256 Гсм Расшифровка - PullRequest
1 голос
/ 11 марта 2020

У меня была такая же проблема в течение двух дней: System.Security.Cryptography.CryptographicException: неверный параметр

в System.Security.Cryptography.ProtectedData.Unprotect (Byte [] encryptedData, Byte [] необязательная энтропия, область действия DataProtectionScope)

Я использовал этот проект для своего кода: https://github.com/jabiel/BrowserPass/tree/master/BrowserPass

В этом проекте ошибка должна быть в ChromePassReader.cs, строка 42, наверное.

На моем компьютере все работало нормально, мой друг заставил меня попробовать его на своем компьютере, и он у него не работает. Я пробовал и на других ПК, но безуспешно.

Наиболее подходящий ответ (предложенный Topaco): Используемый мной тип дешифрования полезен для Data Encrypted with DPAPI. Начиная с версии 80.0 * или выше Chrome данные пароля шифруются с использованием Aes 256 Гсм, поэтому:

Обновление

Я пытался написать код для расшифровки пароля AesGcm256 данные. Я получаю пользовательские данные из базы данных, расположенной в папках Google Chrome, которая называется Login Data. Затем я должен расшифровать пароль, полученный от AesGcm256 Decryption, но я не могу этого сделать. Это моя попытка:

Где я получаю URL-адреса, имена пользователей, пароли:

using System;
using System.Collections.Generic;
using System.Net;
using System.Data.SQLite;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.IO;
using System.Security.Cryptography;
using System.Diagnostics;
using SalsaClient.Algorithm;

namespace SalsaClient.CDS
{
    class ChromePassReader : IPassReader
    {
        public string BrowserName { get { return "Chrome"; } }

        private const string LOGIN_DATA_PATH = "\\..\\Local\\Google\\Chrome\\User Data\\Default\\Login Data";


        public IEnumerable<CredentialModel> ReadPasswords()
        {
            var result = new List<CredentialModel>();

            var appdata = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData);// APPDATA
            var p = Path.GetFullPath(appdata + LOGIN_DATA_PATH);

            if (File.Exists(p))
            {
                Process[] chromeInstances = Process.GetProcessesByName("chrome");
                foreach (Process proc in chromeInstances)
                    proc.Kill();

                using (var conn = new SQLiteConnection($"Data Source={p};"))
                {
                    conn.Open();
                    using (var cmd = conn.CreateCommand())
                    {
                        cmd.CommandText = "SELECT action_url, username_value, password_value FROM logins";
                        using (var reader = cmd.ExecuteReader())
                        {

                            if (reader.HasRows)
                            {
                                while (reader.Read())
                                {

                                    var pass = AesGcm256.decrypt(GetBytes(reader, 2)); //encrypted data

                                    result.Add(new CredentialModel()
                                    {
                                        Url = reader.GetString(0),
                                        Username = reader.GetString(1),
                                        Password = pass
                                    });

                                }
                            }
                        }
                    }
                    conn.Close();
                }

            }
            else
            {
                throw new FileNotFoundException("Cannot find chrome logins file");
            }
            return result;
        }

        private byte[] GetBytes(SQLiteDataReader reader, int columnIndex)
        {
            const int CHUNK_SIZE = 2 * 1024;
            byte[] buffer = new byte[CHUNK_SIZE];
            long bytesRead;
            long fieldOffset = 0;
            using (MemoryStream stream = new MemoryStream())
            {
                while ((bytesRead = reader.GetBytes(columnIndex, fieldOffset, buffer, 0, buffer.Length)) > 0)
                {
                    stream.Write(buffer, 0, (int)bytesRead);
                    fieldOffset += bytesRead;
                }
                return stream.ToArray();
            }
        }
    }
}

Алгоритм:

using Newtonsoft.Json;
using Org.BouncyCastle.Crypto.Engines;
using Org.BouncyCastle.Crypto.Modes;
using Org.BouncyCastle.Crypto.Parameters;
using System;
using System.IO;
using System.Linq;
using System.Security.Cryptography;
using System.Text;

namespace SalsaClient.Algorithm
{
    class AesGcm256
    {
        public static string GetKey()
        {
            string sR = string.Empty;
            var appdata = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData);// APPDATA
            var path = Path.GetFullPath(appdata + "\\..\\Local\\Google\\Chrome\\User Data\\Local State");

            string v = File.ReadAllText(path);

            dynamic json = JsonConvert.DeserializeObject(v);
            string key = json.os_crypt.encrypted_key;

            byte[] src = Convert.FromBase64String(key);
            byte[] encryptedKey = src.Skip(5).ToArray();

            byte[] data = Convert.FromBase64String(encodedString);
            string decodedString = Encoding.UTF8.GetString(data);

            byte[] decryptedKey = ProtectedData.Unprotect(encryptedKey, null, DataProtectionScope.CurrentUser);
        }

        public static string decrypt(string EncryptedText, byte[] key, byte[] iv)
        {
            string sR = string.Empty;
            try
            {
                byte[] encryptedBytes = Convert.FromBase64String(EncryptedText);

                GcmBlockCipher cipher = new GcmBlockCipher(new AesFastEngine());
                AeadParameters parameters = new AeadParameters(new KeyParameter(key), 128, iv, null);

                cipher.Init(false, parameters);
                byte[] plainBytes = new byte[cipher.GetOutputSize(encryptedBytes.Length)];
                Int32 retLen = cipher.ProcessBytes(encryptedBytes, 0, encryptedBytes.Length, plainBytes, 0);
                cipher.DoFinal(plainBytes, retLen);

                sR = Encoding.UTF8.GetString(plainBytes).TrimEnd("\r\n\0".ToCharArray());
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
                Console.WriteLine(ex.StackTrace);
            }

            return sR;
        }
    }
}

1 Ответ

0 голосов
/ 17 марта 2020

К сожалению, вы не описали, что именно не работает. Однако большая часть кода, кажется, реализована правильно. В некоторых частях необходимо внести незначительные изменения и дополнения:

  • Расшифровка DPAPI ключа AES происходит в SalsaClient.Algorithm.AesGcm256.GetKey(). Здесь обратное утверждение отсутствует. Возвращаемое значение имеет тип string. Поскольку ключ обычно состоит из произвольных двоичных данных, следует использовать подходящую кодировку, такую ​​как Base64 или шестнадцатеричное, если данные должны быть возвращены в виде строки. В качестве альтернативы ключ может быть возвращен как byte[], как в следующей адаптации:

    public static byte[] GetKey()
    {
        string sR = string.Empty;
        var appdata = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData);// APPDATA
        var path = Path.GetFullPath(appdata + "\\..\\Local\\Google\\Chrome\\User Data\\Local State");
    
        string v = File.ReadAllText(path);
    
        dynamic json = JsonConvert.DeserializeObject(v);
        string key = json.os_crypt.encrypted_key;
    
        byte[] src = Convert.FromBase64String(key);
        byte[] encryptedKey = src.Skip(5).ToArray();
    
        byte[] decryptedKey = ProtectedData.Unprotect(encryptedKey, null, DataProtectionScope.CurrentUser);
    
        return decryptedKey;
    }
    
  • Далее, SalsaClient.CDSChromePassReader#ReadPasswords() должен быть слегка изменен:

    • Ключ AES должен быть определен.
    • Одноразовый номер и фактический зашифрованный текст должны быть определены из данных, считанных из БД.
    • Расшифровка паролей должна выполняться (с использованием AES ключ, одноразовый номер и фактический зашифрованный текст).


    В целом:

    ...  
    byte[] key = AesGcm256.GetKey();
    while (reader.Read())
    {
        byte[] encryptedData = GetBytes(reader, 2);
        byte[] nonce, ciphertextTag;
        AesGcm256.prepare(encryptedData, out nonce, out ciphertextTag);
        string pass = AesGcm256.decrypt(ciphertextTag, key, nonce); 
        ...
    }
    ...
    
  • Определение одноразового и фактического зашифрованного текста выполняется в новом методе SalsaClient.Algorithm.AesGcm256.prepare(). На этом этапе требуется информация из связанной статьи : данные, связанные с паролем, состоят из следующих частей:

    • Первые три байта являются кодировкой ASCII v10 (0x763130).
    • Следующие 12 байтов являются одноразовыми.
    • Далее следует фактический зашифрованный текст.
    • Последние 16 байтов являются тегом аутентификации режима GCM.


    Фактический зашифрованный текст и тег аутентификации не нужно разделять, поскольку реализация CES / B C AES-GCM обрабатывает обе части вместе:

    public static void prepare(byte[] encryptedData, out byte[] nonce, out byte[] ciphertextTag)
    {
        nonce = new byte[12];
        ciphertextTag = new byte[encryptedData.Length - 3 - nonce.Length];
    
        System.Array.Copy(encryptedData, 3, nonce, 0, nonce.Length);
        System.Array.Copy(encryptedData, 3 + nonce.Length, ciphertextTag, 0, ciphertextTag.Length);
    }
    
  • Поскольку зашифрованные данные (фактический зашифрованный текст и тег аутентификации) представлены в двоичном формате, более удобно передавать эти данные в SalsaClient.Algorithm.AesGcm256.decrypt() как byte[], а не как string:

    public static string decrypt(byte[] encryptedBytes, byte[] key, byte[] iv)
    {
        string sR = string.Empty;
        try
        {
            GcmBlockCipher cipher = new GcmBlockCipher(new AesFastEngine());
            ...
    

    Дешифрованные данные обрезаются в размещенном коде в конце (разрывы строк и 0-значения). На самом деле это не должно быть необходимым. Но, возможно, у вас есть особые причины для этого.

  • На данный момент все необходимые изменения были внесены. Затем расшифровка паролей выполняется с помощью метода Main:

    SalsaClient.CDS.ChromePassReader chromePassReader = new SalsaClient.CDS.ChromePassReader();
    IEnumerable<CredentialModel> credentialList = chromePassReader.ReadPasswords();
    

С помощью этого кода я могу расшифровать пароли Chrome на моем компьютере (в результате чего моя БД содержит только зашифрованные пароли AES-GCM). Однако обратите внимание, что не все пароли должны быть зашифрованы AES-GCM. Старые пароли (начиная с версии v80) могут быть зашифрованы DPAPI. Конечно, они не могут быть расшифрованы описанным способом, но должны быть дешифрованы DPAPI. Как упоминалось выше, пароли, зашифрованные с помощью AES-GCM, могут быть идентифицированы по тому факту, что они начинаются с кодировки ASCII v10 (0x763130). Возможно, вам необходимо добавить соответствующее различие в регистре к вашему коду (по крайней мере, если зашифрованные пароли DPAPI все еще хранятся в БД).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...