Skip to content

Instantly share code, notes, and snippets.

@k4nfr3
Created March 1, 2024 17:25
Show Gist options
  • Save k4nfr3/2644ebadb7a7d5db038c9d73d3db4da0 to your computer and use it in GitHub Desktop.
Save k4nfr3/2644ebadb7a7d5db038c9d73d3db4da0 to your computer and use it in GitHub Desktop.
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