Moving passwords.
Options
Is there a way to migrate my %localappdata%\LINQPad\Passwords
to a new laptop? Or backup this file in my 1Password?
I am not familiar with how DPAPI decrypts these passwords. I assume the key to them is somewhere I would not be able to migrate?
Comments
-
There is no export/import feature - you would have to write this yourself. If you want to go down this path, press Shift+Alt+R to bring up ILSpy, then take a look at LINQPad's PasswordManager class - this has the info you need.
-
Having had to just do this myself, thought I'd post here.
void Main() { Util.HtmlHead.AddStyles("table tr td:nth-child(2) { word-wrap: break-word; max-width: 1000px }"); var userDataFiles = new DirectoryInfo(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "LINQPad", "UserData")) .Dump("UserData Folder Path") .EnumerateFiles() .OrderByDescending(x=>x.LastWriteTime); userDataFiles.Select(x => new { x.Name, Content = x.OpenText().Using(f => { var str = new char[250]; f.ReadBlock(str, 0, (int)Math.Min(f.BaseStream.Length, 250L)); var sb = new StringBuilder().Append(new string(str).TrimEnd('\0')) .Append(f.BaseStream.Length > 250 ? "..." : string.Empty); return sb.ToString(); }), LastWrite = x.LastWriteTime.Humanize(), File = new Hyperlink("Open", _ => Shortcuts.OpenNotepadPP(x.FullName)) }) .Dump("LoadString keys"); new Button("Export User Data", _ => { new TextArea(JsonConvert.SerializeObject(userDataFiles.ToDictionary(df => df.Name, df => Convert.ToBase64String(df.ReadAllBytes())))).Dump(); }).Dump(); var passwords = new DirectoryInfo(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "LINQPad", "Passwords")).Dump("Passwords Folder Path") .EnumerateFiles() .OrderByDescending(x => x.LastWriteTime) .Select(x => { var name = Encoding.UTF8.GetString(Enumerable.Range(0, x.Name.Length) .Where(x => x % 2 == 0) .Select(i => Convert.ToByte(x.Name.Substring(i, 2), 16)) .ToArray()); return new { Name = name, LastWrite = x.LastWriteTime.Humanize(), Content = Util.OnDemand("Click to show", () => Util.GetPassword(name)) }; }) .Dump("Passwords"); new Button("Export Passwords", _ => { new TextArea(JsonConvert.SerializeObject(passwords.ToDictionary(df => df.Name, df => Convert.ToBase64String(Encoding.UTF8.GetBytes(Util.GetPassword(df.Name)))))).Dump(); }).Dump(); } public static class Shortcuts { static string Quote(string s) => s.Contains(" ") ? $"\"{s}\"" : s; public static void OpenNotepadPP(string f) { var progDir = Environment.GetEnvironmentVariable("ProgramFiles(x86)"); var npDir = Path.Combine("Notepad++", "notepad++.exe"); Util.Cmd($"{Quote(Path.Combine(progDir, npDir))} {Quote(f)}"); } public static byte[] ReadAllBytes(this FileInfo f) => !f.Exists ? Array.Empty<byte>() : File.ReadAllBytes(f.FullName); public static TReturn? Using<TDisposable, TReturn>(this TDisposable disposable, Func<TDisposable, TReturn> workFunc) where TDisposable : IDisposable { using (disposable) { return workFunc(disposable); } } }
and to import afterwards:
const string LoadStringType = "Util.LoadString"; const string PasswordType = "Util.Password"; var importType = new SelectBox(SelectBoxKind.DropDown, new[] { LoadStringType, PasswordType }).Dump("Import type"); var ta = new TextArea().Dump("Paste your json here"); new Button("Import",_ => { var dict = JsonConvert.DeserializeObject<Dictionary<string,string>>(ta.Text); Action<string,string> importFunc = importType.SelectedOption switch { LoadStringType => (a,b) => Util.SaveString(a,b), PasswordType => (a,b) => Util.SetPassword(a,b) }; foreach (var pair in dict) { Console.WriteLine("Saving string '{0}'", pair.Key); importFunc(pair.Key, Encoding.UTF8.GetString(Convert.FromBase64String(pair.Value))); } }).Dump();
Export requires the Humanizer nuget package.Both use Newtonsoft.Json, obviously.