[英]How to read Brave Browser cookie database encrypted values in C# (.NET Core)?
我正在嘗試使用 C# 控制台應用程序讀取 cookies 的加密值。
我的 cookie 閱讀器 class
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Security.Cryptography;
using Microsoft.EntityFrameworkCore;
namespace ConsoleApp1.Models
{
public class ChromeCookieReader
{
public IEnumerable<Tuple<string, string>> ReadCookies(string hostName)
{
if (hostName == null) throw new ArgumentNullException("hostName");
using var context = new ChromeCookieDbContext();
var cookies = context
.Cookies
.Where(c => c.HostKey.Equals("localhost"))
.AsNoTracking();
foreach (var cookie in cookies)
{
var decodedData = ProtectedData
.Unprotect(cookie.EncryptedValue,
null,
DataProtectionScope.CurrentUser);
var decodedValue = Encoding.UTF8.GetString(decodedData);
yield return Tuple.Create(cookie.Name, decodedValue);
}
}
}
}
我的 EF DbContext
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations.Schema;
using System.Text;
using Microsoft.EntityFrameworkCore;
namespace ConsoleApp1.Models
{
public class Cookie
{
[Column("host_key")]
public string HostKey { get; set; }
[Column("name")]
public string Name { get; set; }
[Column("encrypted_value")]
public byte[] EncryptedValue { get; set; }
}
public class ChromeCookieDbContext : DbContext
{
public DbSet<Cookie> Cookies { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
// var dbPath = Environment.GetFolderPath(
// Environment.SpecialFolder.LocalApplicationData)
// + @"\Google\Chrome\User Data\Default\Cookies";
var dbPath = Environment.GetFolderPath(
Environment.SpecialFolder.LocalApplicationData)
+ @"\BraveSoftware\Brave-Browser\User Data\Default\Cookies";
if (!System.IO.File.Exists(dbPath)) throw new System.IO.FileNotFoundException("Cant find cookie store", dbPath); // race condition, but i'll risk it
var connectionString = "Data Source=" + dbPath + ";Mode=ReadOnly;";
optionsBuilder
.UseSqlite(connectionString);
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Cookie>().ToTable("cookies").HasNoKey();
}
}
}
我嘗試的解決方案受到Chrome 中的 Encrypted cookies 的啟發,但是盡管 Brave Browser 基於 Chromium,但它看起來並不相同。 相反,Windows 數據保護 API 會引發異常。
Internal.Cryptography.CryptoThrowHelper.WindowsCryptographicException
HResult=0x0000000D
Message=The data is invalid.
Source=System.Security.Cryptography.ProtectedData
StackTrace:
at System.Security.Cryptography.ProtectedData.ProtectOrUnprotect(Byte[] inputData, Byte[] optionalEntropy, DataProtectionScope scope, Boolean protect)
at System.Security.Cryptography.ProtectedData.Unprotect(Byte[] encryptedData, Byte[] optionalEntropy, DataProtectionScope scope)
at ConsoleApp1.Models.ChromeCookieReader.<ReadCookies>d__0.MoveNext()
其他已知問題:如果 Brave 已打開,EF Core 會“嚇壞”SQLite 數據庫被鎖定並且不會讀取任何內容。
在 Chromium 80 及更高版本中,Google 修改了 cookies 的加密方式,為用戶提供額外的安全性。 您不能再將 cookies 直接傳遞給 Windows DPAPI 進行解密。 而是 Chrome 的本地 State 存儲使用 Windows DPAI 解密的加密密鑰,您必須使用該密鑰來解密 cookies。 我在應得的地方給予信任,因為我自己沒有發現這一點,並使用https://stackoverflow.com/a/60611673/6481581的答案中的信息來解決我的問題。
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Linq;
using Microsoft.EntityFrameworkCore;
using System.Security.Cryptography;
using Newtonsoft.Json.Linq;
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Crypto.Engines;
using Org.BouncyCastle.Crypto.Modes;
using Org.BouncyCastle.Crypto.Parameters;
namespace BraveBrowserCookieReaderDemo
{
public class BraveCookieReader
{
public IEnumerable<Tuple<string, string>> ReadCookies(string hostName)
{
if (hostName == null) throw new ArgumentNullException("hostName");
using var context = new BraveCookieDbContext();
var cookies = context
.Cookies
.Where(c => c.HostKey.Equals(hostName))
.AsNoTracking();
// Big thanks to https://stackoverflow.com/a/60611673/6481581 for answering how Chrome 80 and up changed the way cookies are encrypted.
string encKey = File.ReadAllText(System.Environment.GetEnvironmentVariable("LOCALAPPDATA") + @"\BraveSoftware\Brave-Browser\User Data\Local State");
encKey = JObject.Parse(encKey)["os_crypt"]["encrypted_key"].ToString();
var decodedKey = System.Security.Cryptography.ProtectedData.Unprotect(Convert.FromBase64String(encKey).Skip(5).ToArray(), null, System.Security.Cryptography.DataProtectionScope.LocalMachine);
foreach (var cookie in cookies)
{
var data = cookie.EncryptedValue;
var decodedValue = _decryptWithKey(data, decodedKey, 3);
yield return Tuple.Create(cookie.Name, decodedValue);
}
}
private string _decryptWithKey(byte[] message, byte[] key, int nonSecretPayloadLength)
{
const int KEY_BIT_SIZE = 256;
const int MAC_BIT_SIZE = 128;
const int NONCE_BIT_SIZE = 96;
if (key == null || key.Length != KEY_BIT_SIZE / 8)
throw new ArgumentException(String.Format("Key needs to be {0} bit!", KEY_BIT_SIZE), "key");
if (message == null || message.Length == 0)
throw new ArgumentException("Message required!", "message");
using (var cipherStream = new MemoryStream(message))
using (var cipherReader = new BinaryReader(cipherStream))
{
var nonSecretPayload = cipherReader.ReadBytes(nonSecretPayloadLength);
var nonce = cipherReader.ReadBytes(NONCE_BIT_SIZE / 8);
var cipher = new GcmBlockCipher(new AesEngine());
var parameters = new AeadParameters(new KeyParameter(key), MAC_BIT_SIZE, nonce);
cipher.Init(false, parameters);
var cipherText = cipherReader.ReadBytes(message.Length);
var plainText = new byte[cipher.GetOutputSize(cipherText.Length)];
try
{
var len = cipher.ProcessBytes(cipherText, 0, cipherText.Length, plainText, 0);
cipher.DoFinal(plainText, len);
}
catch (InvalidCipherTextException)
{
return null;
}
return Encoding.Default.GetString(plainText);
}
}
}
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.