Skip to content

Instantly share code, notes, and snippets.

@swaters86
Created February 20, 2026 14:26
Show Gist options
  • Select an option

  • Save swaters86/691c8423bed217909f443027518a6e62 to your computer and use it in GitHub Desktop.

Select an option

Save swaters86/691c8423bed217909f443027518a6e62 to your computer and use it in GitHub Desktop.
// DpapiRegistrySecrets.Net48.cs
// Target: .NET Framework 4.8 (C# 7.x) (Windows only)
// No NuGet needed for DPAPI on .NET Framework.
using System;
using System.Security.Cryptography;
using System.Text;
using Microsoft.Win32;
public static class DpapiRegistrySecrets
{
// Keep stable once deployed; changing breaks decryption of existing values.
private const string DefaultPurpose = "YourCompany.Connector.Secrets.v1";
// --------- One-liner DPAPI helpers ---------
public static string ProtectToBase64(string plaintext, string purpose = null, bool localMachine = true)
{
EnsureWindows();
if (plaintext == null) return null;
byte[] entropy = Encoding.UTF8.GetBytes(purpose ?? DefaultPurpose);
byte[] data = Encoding.UTF8.GetBytes(plaintext);
var scope = localMachine ? DataProtectionScope.LocalMachine : DataProtectionScope.CurrentUser;
byte[] protectedBytes = ProtectedData.Protect(data, entropy, scope);
return Convert.ToBase64String(protectedBytes);
}
public static string UnprotectFromBase64(string protectedBase64, string purpose = null, bool localMachine = true)
{
EnsureWindows();
if (string.IsNullOrWhiteSpace(protectedBase64)) return null;
byte[] entropy = Encoding.UTF8.GetBytes(purpose ?? DefaultPurpose);
byte[] protectedBytes = Convert.FromBase64String(protectedBase64);
var scope = localMachine ? DataProtectionScope.LocalMachine : DataProtectionScope.CurrentUser;
byte[] bytes = ProtectedData.Unprotect(protectedBytes, entropy, scope);
return Encoding.UTF8.GetString(bytes);
}
// --------- Registry storage helpers ---------
// Recommended for Windows Services: RegistryHive.LocalMachine + localMachineDpapi = true
public static void SaveSecret(
RegistryHive hive,
string subKeyPath,
string valueName,
string plaintext,
string purpose = null,
bool localMachineDpapi = true,
RegistryView view = RegistryView.Registry64)
{
EnsureWindows();
if (string.IsNullOrWhiteSpace(subKeyPath)) throw new ArgumentException("subKeyPath is required.", "subKeyPath");
if (string.IsNullOrWhiteSpace(valueName)) throw new ArgumentException("valueName is required.", "valueName");
string protectedBase64 = ProtectToBase64(plaintext, purpose, localMachineDpapi);
using (var baseKey = RegistryKey.OpenBaseKey(hive, view))
using (var key = baseKey.CreateSubKey(subKeyPath, writable: true))
{
if (key == null)
throw new InvalidOperationException("Failed to create/open registry key: " + hive + "\\" + subKeyPath);
key.SetValue(valueName, protectedBase64 ?? string.Empty, RegistryValueKind.String);
}
}
public static string LoadSecret(
RegistryHive hive,
string subKeyPath,
string valueName,
string purpose = null,
bool localMachineDpapi = true,
RegistryView view = RegistryView.Registry64)
{
EnsureWindows();
if (string.IsNullOrWhiteSpace(subKeyPath)) throw new ArgumentException("subKeyPath is required.", "subKeyPath");
if (string.IsNullOrWhiteSpace(valueName)) throw new ArgumentException("valueName is required.", "valueName");
using (var baseKey = RegistryKey.OpenBaseKey(hive, view))
using (var key = baseKey.OpenSubKey(subKeyPath, writable: false))
{
if (key == null) return null;
var protectedBase64 = key.GetValue(valueName) as string;
if (string.IsNullOrWhiteSpace(protectedBase64)) return null;
return UnprotectFromBase64(protectedBase64, purpose, localMachineDpapi);
}
}
public static void DeleteSecret(
RegistryHive hive,
string subKeyPath,
string valueName,
RegistryView view = RegistryView.Registry64)
{
EnsureWindows();
using (var baseKey = RegistryKey.OpenBaseKey(hive, view))
using (var key = baseKey.OpenSubKey(subKeyPath, writable: true))
{
if (key == null) return;
key.DeleteValue(valueName, throwOnMissingValue: false);
}
}
private static void EnsureWindows()
{
// .NET Framework has no OperatingSystem.IsWindows() and no RuntimeInformation by default.
var p = Environment.OSVersion.Platform;
var isWindows =
p == PlatformID.Win32NT ||
p == PlatformID.Win32Windows ||
p == PlatformID.Win32S ||
p == PlatformID.WinCE;
if (!isWindows)
throw new PlatformNotSupportedException("DPAPI/Registry secrets are supported only on Windows.");
}
}
/*
Example usage (.NET 4.8 Windows Service):
var keyPath = @"SOFTWARE\YourCompany\Connector\Secrets";
// Save (typically done by installer or enrollment tool running elevated)
DpapiRegistrySecrets.SaveSecret(
hive: RegistryHive.LocalMachine,
subKeyPath: keyPath,
valueName: "CognitoClientSecret",
plaintext: "super-secret",
purpose: "YourCompany.Cognito.ClientSecret.v1",
localMachineDpapi: true);
// Load (service runtime)
var secret = DpapiRegistrySecrets.LoadSecret(
RegistryHive.LocalMachine,
keyPath,
"CognitoClientSecret",
purpose: "YourCompany.Cognito.ClientSecret.v1",
localMachineDpapi: true);
*/
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment