【问题标题】:How to read Brave Browser cookie database encrypted values in C# (.NET Core)?如何在 C# (.NET Core) 中读取 Brave Browser cookie 数据库加密值?
【发布时间】:2020-07-31 17:00:22
【问题描述】:

我正在尝试使用 C# 控制台应用程序读取 cookie 的加密值。

我的 cookie 阅读器类

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

我尝试的解决方案受到Encrypted cookies in Chrome 的启发,但是尽管 Brave 浏览器基于 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 数据库被锁定并且不会读取任何内容。

【问题讨论】:

    标签: c# windows google-chrome dpapi brave-browser


    【解决方案1】:

    在 Chromium 80 及更高版本中,Google 修改了 cookie 的加密方式,以向用户提供额外的安全性。您不能再将 cookie 直接传递给 Windows DPAPI 以进行解密。而是 Chrome 的本地状态存储使用 Windows DPAI 解密的加密密钥,您必须使用该密钥来解密 cookie。我在应得的地方给予信任,因为我自己没有发现这一点,并使用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);
                }
            }
        }
    }
    

    【讨论】:

      猜你喜欢
      • 2020-02-01
      • 2021-09-18
      • 2017-06-22
      • 2013-11-30
      • 1970-01-01
      • 1970-01-01
      • 2014-10-19
      • 2020-06-15
      • 1970-01-01
      相关资源
      最近更新 更多