{"id":247,"date":"2022-10-30T07:02:14","date_gmt":"2022-10-30T07:02:14","guid":{"rendered":"https:\/\/www-users.tebibyte.io\/~yihanwu1024\/?p=247"},"modified":"2023-12-29T17:25:15","modified_gmt":"2023-12-29T17:25:15","slug":"chromium-browser-data-migration-on-windows","status":"publish","type":"post","link":"https:\/\/www.tebibyte.io\/~yihanwu1024\/2022\/chromium-browser-data-migration-on-windows\/","title":{"rendered":"Chromium Browser Data Migration on Windows"},"content":{"rendered":"\n<p>Modern Chromium utilizes the Windows Data Protection API (DPAPI), specifically <code>CryptProtectData()<\/code>. Data migration cannot be done by simply copying the data directory to another computer, because the data would be encrypted with a key only available on the source computer. Fortunately, it turns out to be fairly simple to migrate Chromium user data, thanks to the design that Chromium envelops the encryption mechanism by one layer. The function <code>CryptProtectData()<\/code> does not encrypt the data directly, but rather a standalone AES key in the <code>Local State<\/code> file, and this key is in turn used to encrypt confidential data such as cookies and website credentials. Therefore, one easy way to migrate Chromium is to decrypt this Local State key and reencrypt it within the new environment. The process is simple enough to perform manually.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">On the source computer<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">TL; DR; Copy; Paste<\/h3>\n\n\n\n<p>First, import <code>ProtectedData<\/code> to PowerShell as described in the immediate next section. Next, in your Chromium User Data directory (where there is a <code>Local State<\/code> file):<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>$json = (Get-Content '.\\Local State' -Encoding UTF8 | ConvertFrom-Json); $encrypted_key = $json.os_crypt.encrypted_key; $encrypted_key_data = &#91;Convert]::FromBase64String($encrypted_key)&#91;5..9999]; &#91;System.Security.Cryptography.ProtectedData]::Unprotect($encrypted_key_data, $null, &#91;System.Security.Cryptography.DataProtectionScope]::CurrentUser) | Out-File '.\\Local State Unprotected Key'<\/code><\/pre>\n\n\n\n<p>The individual steps for the emigration are below. After you are done with emigration, go to the next section to perform immigration.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Import <code>ProtectedData<\/code> to PowerShell<\/h3>\n\n\n\n<p><code>System.Security.Cryptography.ProtectedData<\/code> is a Microsoft .NET Core library that handles DPAPI calls. You can <a rel=\"noreferrer noopener\" href=\"https:\/\/www.nuget.org\/packages\/System.Security.Cryptography.ProtectedData\/4.7.0\" target=\"_blank\">download it from NuGet<\/a>. The following procedure has been tested with version 4.6.1 of the library.<\/p>\n\n\n\n<p>.nupkg packages are zip files. Change the extension and extract a <code>System.Security.Cryptography.ProtectedData.dll<\/code> file.<\/p>\n\n\n\n<p>Then, open a Windows PowerShell window and use the following command to import the library to the current session. You may need to change your PowerShell execution policy and unblock the downloaded file for this step.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Add-Type -Path &lt;Path to library&gt;<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Get the <code>encrypted_key<\/code> value from Chromium <code>Local State<\/code> file<\/h3>\n\n\n\n<p>Navigate to your Chromium user data directory. For example, Chrome is at <code>%USERPROFILE%\\AppData\\Local\\Google\\Chrome\\User Data\\<\/code>. Open <code>Local State<\/code> with a text editor. This file is a giant JSON. Search for the <code>encrypted_key<\/code> key, and copy down its value (from after the colon and double quote to before the next double quote.)<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Transform the <code>encrypted_key<\/code> value<\/h3>\n\n\n\n<p>Your <code>encrypted_key<\/code> value is in base64 and has an extra header inside. Turn it into a raw byte array and remove the header with<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>$encrypted_key_data = &#91;Convert]::FromBase64String($encrypted_key)&#91;5..9999]<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Unprotect the data<\/h3>\n\n\n\n<p>Now the data is ready to be processed with DPAPI. Do this:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>$unprotected_key = &#91;System.Security.Cryptography.ProtectedData]::Unprotect($encrypted_key_data, $null, &#91;System.Security.Cryptography.DataProtectionScope]::CurrentUser)<\/code><\/pre>\n\n\n\n<p>It should be 128 bits (32 bytes) as of Chromium 107.<\/p>\n\n\n\n<p>Somehow note down the value of <code>$unprotected_key<\/code>.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">On the target computer<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">TL; DR; Copy; Paste<\/h3>\n\n\n\n<p>You still need to import <code>ProtectedData<\/code>. Then, in your Chromium User Data directory (where there are <code>Local State<\/code> and <code>Local State Unprotected Key<\/code> files created in the previous step):<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>$unprotected_key = &#91;byte&#91;]] (Get-Content '.\\Local State Unprotected Key'); $encrypted_key_data = &#91;System.Security.Cryptography.ProtectedData]::Protect($unprotected_key, $null, &#91;System.Security.Cryptography.DataProtectionScope]::CurrentUser); $encrypted_key_data = &#91;byte]68,80,65,80,73 + $encrypted_key_data; $encrypted_key = &#91;Convert]::ToBase64String($encrypted_key_data); $json = (Get-Content '.\\Local State' -Encoding UTF8 | ConvertFrom-Json); $json.os_crypt.encrypted_key = $encrypted_key; ConvertTo-Json -InputObject $json -Depth 8 -Compress | Out-File 'Local State' -Encoding utf8<\/code><\/pre>\n\n\n\n<p>You might want to delete the migration file <code>Local State Unprotected Key<\/code>.<\/p>\n\n\n\n<p>The individual steps are below.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Import <code>ProtectedData<\/code> to PowerShell<\/h3>\n\n\n\n<p>Yeah, you have to do it again.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Protect the data<\/h3>\n\n\n\n<p>I believe you have typed your <code>$unprotected_key<\/code> carefully into PowerShell. Make sure it is a byte array. Then, do this:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>$encrypted_key_data = &#91;System.Security.Cryptography.ProtectedData]::Protect($unprotected_key, $null, &#91;System.Security.Cryptography.DataProtectionScope]::CurrentUser)<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Transform to an <code>encrypted_key<\/code> value<\/h3>\n\n\n\n<p>The following command adds that header back:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>$encrypted_key_data = &#91;byte]68,80,65,80,73 + $encrypted_key_data<\/code><\/pre>\n\n\n\n<p>And this one converts it to base64:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>$encrypted_key = &#91;Convert]::ToBase64String($encrypted_key_data)<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Edit <code>Local State<\/code> with new <code>encrypted_key<\/code> value<\/h3>\n\n\n\n<p>Now you can close Chromium, copy your data, and change the <code>encrypted_key<\/code> value to what you got in the previous step.<\/p>\n\n\n\n<p>Chromium migration is complete.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Wait, does this mean my Chromium (Electron) data is available to all programs running as the current user?<\/h2>\n\n\n\n<p>Yes.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Modern Chromium utilizes the Windows Data Protection API (DPAPI), specifically CryptProtectData(). Data migration cannot be done by simply copying the data directory to another computer, because the data would be encrypted with a key only available on the source computer. Fortunately, it turns out to be fairly simple to migrate Chromium user data, thanks to the design that Chromium envelops the encryption mechanism by one layer.<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[5],"tags":[7],"class_list":["post-247","post","type-post","status-publish","format-standard","hentry","category-systems-administration","tag-en"],"_links":{"self":[{"href":"https:\/\/www.tebibyte.io\/~yihanwu1024\/wp-json\/wp\/v2\/posts\/247","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.tebibyte.io\/~yihanwu1024\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.tebibyte.io\/~yihanwu1024\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.tebibyte.io\/~yihanwu1024\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.tebibyte.io\/~yihanwu1024\/wp-json\/wp\/v2\/comments?post=247"}],"version-history":[{"count":0,"href":"https:\/\/www.tebibyte.io\/~yihanwu1024\/wp-json\/wp\/v2\/posts\/247\/revisions"}],"wp:attachment":[{"href":"https:\/\/www.tebibyte.io\/~yihanwu1024\/wp-json\/wp\/v2\/media?parent=247"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.tebibyte.io\/~yihanwu1024\/wp-json\/wp\/v2\/categories?post=247"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.tebibyte.io\/~yihanwu1024\/wp-json\/wp\/v2\/tags?post=247"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}