Created
March 1, 2024 17:25
-
-
Save k4nfr3/2644ebadb7a7d5db038c9d73d3db4da0 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add NugetComponent Microsoft.Win32.Registry | |
Add NugetComponent System.Security.Cryptography.ProtectedData | |
Program.cs based on https://github.com/sergeig888/csharp-dpapi-PBIE/ | |
Tested on lates version Kiteworks 8.3.0 | |
========================================= | |
/* Created by Sergei Gundorov 1/2/2020 | |
* Intent: provide sample project for encrypting secrets with DPAPI while working with | |
* Power BI Embedded and API tutorials and samples. | |
* | |
* Power BI embedded calls for supplying credetials of AAD user (or service principal) to obtain the access token. | |
* Very insecure flow if secrets are kept in the code or config file in plain text. | |
* Password/secret protection should be applied even in exploratory projects. | |
*/ | |
using System; | |
using System.IO; | |
using System.Reflection; | |
using System.Security.Cryptography; | |
using System.Text; | |
using Microsoft.Win32; | |
namespace EncryptCredential | |
{ | |
class Program | |
{ | |
//NOTE: many .Net Framework cryptographic samples reference default codepage which could lead to all sorts of | |
//hard to troubleshoot problems. It is best to set the exact codepage for your specifc configuration and environment | |
//NOTE: the line below can be used with .Net Framework implementaion if there is a need to change codepage without | |
//requiring recompilation; use of .Net Core 3.1 self-contained executable file in my case makes the use of appconfig unnecessarily hard | |
//private static Encoding codePage = Encoding.GetEncoding(Convert.ToInt32(ConfigurationManager.AppSettings["codePage"])); | |
//setting code page to what works in both .Net and .Net Core; .Default is not considered best practice | |
private static Encoding codePage = Encoding.GetEncoding(28591); | |
//NOTE: file path is hardcoded because .Net Core doesn't work well with extenal config file with self-contained exe files | |
//user of the console app can always specify alternative file name | |
//private static string secretsFile = "C:\\temp\\Encoded64BitSecret.bin"; | |
private static string secretsFile = "C:\\Users\\fbussink\\AppData\\Local\\Accellion\\AccellionOutlook\\localsettings.cfg"; | |
private static string secretsStore; | |
private static DataProtectionScope protScope; | |
static void Main(string[] args) | |
{ | |
Console.WriteLine("1 - encrypt or 2 - decrypt (using codepage {0}):", codePage.CodePage); | |
int choice = Int32.TryParse(Console.ReadLine(), out choice) ? choice : 1; | |
Console.WriteLine("Specify file path and name:\n(Press [ENTER] for default '{0}')", secretsFile); | |
string userFile = Console.ReadLine(); | |
secretsFile = userFile.Length == 0 ? secretsFile : userFile; | |
try | |
{ | |
if (choice <= 1) | |
{ | |
Console.WriteLine("1 - machine or 2 - user data protection scope:\n(Press [ENTER] for default machine scope)"); | |
int protChoice = Int32.TryParse(Console.ReadLine(), out protChoice) ? protChoice : 1; | |
protScope = protChoice <= 1 ? DataProtectionScope.LocalMachine : DataProtectionScope.CurrentUser; | |
EncryptString(); | |
} | |
DecryptString(); | |
} | |
catch (Exception e) | |
{ | |
Console.Write(e.Message); | |
} | |
} | |
private static void DecryptString() | |
{ | |
Console.WriteLine("Let's decrypt file {0}", secretsFile); | |
string protectedString64Bit = File.ReadAllText(secretsFile); | |
//Console.WriteLine("Base64Content : {0}", protectedString64Bit); | |
//NOTE: DataProtectionScope enum value in decrypt operations doesn't seem to make any difference | |
RegistryKey key = RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Registry64); | |
string MachineGuid = (string)key.OpenSubKey(@"SOFTWARE\Microsoft\Cryptography").GetValue("MachineGuid", "0"); | |
Console.WriteLine("MachineGuid : {0}", MachineGuid); | |
byte[] bytes = Encoding.UTF8.GetBytes(MachineGuid); | |
byte[] plainText = ProtectedData.Unprotect(System.Convert.FromBase64String(protectedString64Bit), bytes, DataProtectionScope.LocalMachine); | |
Console.WriteLine(codePage.GetString(plainText)); | |
if (secretsStore == null) return; | |
} | |
private static void EncryptString() | |
{ | |
Console.WriteLine("Enter test string or hit enter for default:"); | |
//NOTE: the line below can be used to explore what encodings are available on the target system | |
//var enc = Encoding.GetEncodings(); | |
string plainTextPwd = Console.ReadLine(); | |
plainTextPwd = plainTextPwd.Length == 0 ? "password" : plainTextPwd; | |
Console.WriteLine("Plain text password:\n{0}\n", plainTextPwd); | |
byte[] buffer = codePage.GetBytes(plainTextPwd); | |
buffer = ProtectedData.Protect(buffer, null, protScope); | |
string protectedString = codePage.GetString(buffer); | |
//NOTE: conversion to base 64 string is an optional extra step to produce human readable string. | |
//Encrypted bytes can be stored and read in binary format without being converted to base 64 string | |
string protectedString64Bit = System.Convert.ToBase64String(buffer); | |
secretsStore = protectedString; | |
Console.WriteLine("64 bit encoded encrypted string:\n{0}\n", protectedString64Bit); | |
File.WriteAllText(secretsFile, protectedString64Bit); | |
} | |
} | |
} | |
// Example of output | |
C:\temp\Kiteworks_dpapi_decryptor\win-x86>Kitework_session_decryptor.exe | |
1 - encrypt or 2 - decrypt (using codepage 28591): | |
2 | |
Specify file path and name: | |
(Press [ENTER] for default 'C:\Users\myname\AppData\Local\Accellion\AccellionOutlook\localsettings.cfg') | |
C:\Users\myname\AppData\Local\Accellion\AccellionOutlook\users\elktfeeb.jhe.133421\session.cfg | |
Let's decrypt file C:\Users\myname\AppData\Local\Accellion\AccellionOutlook\users\elktfeeb.jhe.133421\session.cfg | |
{"AccessToken":"1234567894516f599aca0bd0c863aa4abf96bd77","RefreshToken":"123456780bedff00ddef61defe076fac93f7d53f","Hostname":"xxx.kiteworks.com","User":{"id":"b60e8373-6f15-4219-ae5b-a10fd8115529","name":"?.?@?.com","active":null,"basedirId":"84005b95-3dea-4f67-ab46-22d4f4685d8d","created":"2024-02-26T13:58:36+01:00","deleted":null,"email":"?.?@?.com","flags":null,"mydirId":"5e49ba08-8751-4166-a1ae-7680ba8a4134","syncdirId":"ad96b68b-87f4-4fd8-bed9-8427fce6640e","userTypeId":1,"verified":null,"internal":false,"password":null,"dbUser":null,"isAdmin":null,"signature":null,"userType":null,"serviceName":"Kiteworks","profileIcon":"b60e8373-6f15-4219-ae5b-a10fd8115529","mailOnlyUser":false,"links":[]},"AccessTokenExpiresIn":"2024-03-29T00:00:00+01:00","PinnedItems":null,"UserCode":"elktfeeb.jhe.133421"} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment