Search code examples
visual-studioencryptionpasswordsvisual-studio-2019webdeploy

How does Visual Studio encrypt passwords in a .pubxml.user file?


When you tell Visual Studio to save your password for a publish profile, it creates a .pubxml.user file next to your publish file looking something like the following:

<?xml version="1.0" encoding="utf-8"?>
<!--
This file is used by the publish/package process of your Web project. You can customize the behavior of this process
by editing this MSBuild file. In order to learn more about this please visit https://go.microsoft.com/fwlink/?LinkID=208121. 
-->
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <PropertyGroup>
    <TimeStampOfAssociatedLegacyPublishXmlFile />
    <EncryptedPassword>AQAAANC[...]</EncryptedPassword>
  </PropertyGroup>
</Project>

How does Visual Studio actually encrypt the password in the EncryptedPassword element? I'd like to decrypt it because I've forgotten my password... now it's just stored encrypted in this file!


Solution

  • The data are DPAPI encrypted. DPAPI encrypted data starts hexadecimal with the byte sequence 0x01000000D08C9DDF0115D1118C7A00C04FC297EB or Base64 encoded with AQAAANCMnd8BFdERjHoAwE/Cl+, here.

    For decryption with C# the class ProtectedData or more precisely the static method ProtectedData.Unprotect can be used. If no value is known for the entropy s_aditionalEntropy, null should be tried. More information about this parameter can be found here.

    If the encrypted data are Base64 encoded, they must be Base64 decoded before decryption:

    using System.Security.Cryptography;
    
    ...
    String encryptedDataB64 = "AQAAANCMnd8BFdERjHoAwE/Cl+...";
    byte[] encryptedData = Convert.FromBase64String(encryptedDataB64); 
    
    byte[] s_aditionalEntropy = null;
    byte[] data = ProtectedData.Unprotect(encryptedData, s_aditionalEntropy, DataProtectionScope.CurrentUser); 
    

    A more detailed example can be found in the linked documentation. The decryption is not limited to .NET, but is also possible with other languages, provided that a corresponding DPAPI-wrapper exists, e.g. in Python with win32crypt.CryptUnprotectData or in Java with Java DPAPI.

    Here's a console program that gets the Unicode string representation of the decoded data:

    using System;
    using System.Security.Cryptography;
    
    namespace ConsolePassDecrypter {
        class Program {
            static void Main(string[] args) {
                string encryptedPass = "AQAAANC[...]";
                var decryptedPassBytes = ProtectedData.Unprotect(Convert.FromBase64String(encryptedPass), null, DataProtectionScope.LocalMachine);
                Console.WriteLine("Decrypted pass: " + System.Text.Encoding.Unicode.GetString(decryptedPassBytes));
            }
        }
    }