Skip to content

Instantly share code, notes, and snippets.

@scriptingstudio
Created April 19, 2026 18:13
Show Gist options
  • Select an option

  • Save scriptingstudio/fb7486dda1f82c3b7ef8dc5021c88b47 to your computer and use it in GitHub Desktop.

Select an option

Save scriptingstudio/fb7486dda1f82c3b7ef8dc5021c88b47 to your computer and use it in GitHub Desktop.
Experimental C# string encryptor
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Security;
using System.Security.Cryptography;
using System.Text.RegularExpressions;
namespace Encryption
{
public class Encryption //: IDisposable
{
private class ProtectedString //: IDisposable
{
internal string EncryptionType;
internal string CipherText;
internal string DPAPIIdentity;
internal DataProtectionScope DPAPIScope;
/*internal struct AESKeyConfig // experimental
{
public string Salt;
public int Iterations;
public HashAlgorithmName Algorithm;
public AESKeyConfig(string salt, int iterations, HashAlgorithmName hash)
{
this.Salt = salt;
this.Iterations = iterations;
this.Algorithm = hash;
}
}; // AESKeyConfig*/
internal ProtectedString(string EncryptionType, string CipherText)
{
this.EncryptionType = EncryptionType;
this.CipherText = CipherText;
this.DPAPIIdentity = null;
this.DPAPIScope = DataProtectionScope.CurrentUser;
} // end constructor
internal ProtectedString(string Protected)
{
if (string.IsNullOrEmpty(Protected)) return;
if (Regex.IsMatch(Protected, "^A"))
{
this.EncryptionType = "AES";
this.CipherText = Protected.Substring(1);
this.DPAPIIdentity = null;
}
else if (Regex.IsMatch(Protected, "^D"))
{
string[] p = Protected.Substring(1).Split('?');
this.CipherText = p[0];
//this.DPAPIScope = p[2]; //TODO
var DPAPIIdBytes = System.Convert.FromBase64String(p[1]);
this.DPAPIIdentity = System.Text.Encoding.UTF8.GetString(DPAPIIdBytes);
this.EncryptionType = "DPAPI";
}
else
{
throw new Exception("ERROR: Does not appear to be a ProtectedString signature.");
}
} // end constructor
internal static string Protect(string InputString, string EncryptionType, DataProtectionScope Scope, byte[] AESKey = null)
{
if (EncryptionType == "DPAPI")
{
var StringBytes = System.Text.Encoding.UTF8.GetBytes(InputString);
var DPAPIBytes = ProtectedData.Protect(StringBytes, null, Scope);
var ConvertedString = System.Convert.ToBase64String(DPAPIBytes);
var Identity = string.Format(@"{0}\{1}", Environment.MachineName, Environment.UserName);
var jsonBytes = System.Text.Encoding.UTF8.GetBytes(Identity);
var EncodedId = System.Convert.ToBase64String(jsonBytes);
return string.Format("D{0}?{1}", ConvertedString, EncodedId); // seal output
//return string.Format("D{0}?{1}?{2}", ConvertedString, Scope, EncodedId);
}
else if (EncryptionType == "AES")
{
byte[] InitializationVector = new byte[16];
var RNG = RandomNumberGenerator.Create();
RNG.GetBytes(InitializationVector);
var AESProvider = Aes.Create(); //AesCryptoServiceProvider.Create();
AESProvider.Key = AESKey;
AESProvider.IV = InitializationVector;
var ClearTextBytes = System.Text.Encoding.UTF8.GetBytes(InputString);
var Encryptor = AESProvider.CreateEncryptor();
var EncryptedBytes = Encryptor.TransformFinalBlock(ClearTextBytes, 0, ClearTextBytes.Length);
byte[] FullData = new byte[AESProvider.IV.Length + EncryptedBytes.Length];
System.Buffer.BlockCopy(AESProvider.IV, 0, FullData, 0, AESProvider.IV.Length);
System.Buffer.BlockCopy(EncryptedBytes, 0, FullData, AESProvider.IV.Length, EncryptedBytes.Length);
AESProvider.Dispose();
return string.Format("A{0}", System.Convert.ToBase64String(FullData)); // seal output
}
else return null;
} // end Protect
internal static string UnProtect(string InputString, byte[] AESKey = null)
{
if (Regex.IsMatch(InputString,"^A"))
{
try
{
var AESProvider = Aes.Create(); //AesCryptoServiceProvider.Create();
AESProvider.Key = AESKey;
var EncryptedBytes = System.Convert.FromBase64String(InputString.Substring(1));
byte[] byte16 = new byte[16];
Array.Copy(EncryptedBytes, 0, byte16, 0, 16); // below C#8
AESProvider.IV = byte16;
var UnencryptedBytes = AESProvider.CreateDecryptor().TransformFinalBlock(EncryptedBytes, 16, EncryptedBytes.Length - 16);
AESProvider.Dispose();
return System.Text.Encoding.UTF8.GetString(UnencryptedBytes);
}
catch
{
Console.WriteLine("Failed to decrypt. Incorrect AES key. Check your Master Password.");
return null;
}
}
else if (Regex.IsMatch(InputString, "^D"))
{
try
{
string encrypted = InputString.Substring(1).Split('?')[0]; // unseal input
byte[] DPAPIBytes = System.Convert.FromBase64String(encrypted);
var DecryptedBytes = ProtectedData.Unprotect(DPAPIBytes, null, DataProtectionScope.CurrentUser); // Scope
return System.Text.Encoding.UTF8.GetString(DecryptedBytes);
}
catch
{
Console.WriteLine("Unable to decrypt as this user/machine on this machine.");
return null;
}
}
else return null;
} // end UnProtect
internal static dynamic ToAESKey(SecureString SecureStringInput, bool ByteArray = false, Hashtable aeskey = null)
{
//if (null == SecureStringInput || SecureStringInput.Length == 0) return null;
var Plaintext = ProtectedString.FromSecureString(SecureStringInput);
var SaltBytes = System.Convert.FromBase64String((string)aeskey["Salt"]);
var PBKDF2 = new Rfc2898DeriveBytes(Plaintext, SaltBytes, (int)aeskey["Iterations"], (HashAlgorithmName)aeskey["Algorithm"]);
// Generate an AES Key
var Key = PBKDF2.GetBytes(32);
SecureString SecureAESKey;
// If the ByteArray switch is provided, return a plaintext byte array,
// otherwise turn the AES key in to a SecureString object
if (ByteArray) return Key;
else SecureAESKey = ProtectedString.ToSecureString(System.BitConverter.ToString(Key));
string HexString = ProtectedString.FromSecureString(SecureAESKey); // done!
if (HexString == "") return null;
string normalized = Regex.Replace(HexString, @"\b0x\B|\\\x78|[\-\,:]", "", RegexOptions.IgnoreCase);
normalized = Regex.Replace(normalized, @"^:+|:+$|[x\\]", "", RegexOptions.IgnoreCase);
return Regex.Split(normalized, "(..)").Where(el => el != "").Select(el => Convert.ToByte(el, 16)).ToArray();
} // ToAESKey
internal static SecureString ToSecureString(string plainstring)
{
// https://stackoverflow.com/questions/1570422/convert-string-to-securestring/38326325#38326325
SecureString secstring = new SecureString();
if (!string.IsNullOrEmpty(plainstring))
Array.ForEach(plainstring.ToArray(), secstring.AppendChar);
return secstring;
} // end ToSecureString
internal static string FromSecureString(SecureString secstring)
{
if (null == secstring || secstring.Length == 0) return "";
/*IntPtr BSTR = IntPtr.Zero;
BSTR = Marshal.SecureStringToBSTR(secstring);
string PlainText = Marshal.PtrToStringBSTR(BSTR);
Marshal.ZeroFreeBSTR(BSTR);
return PlainText;*/
IntPtr valuePtr = IntPtr.Zero;
try
{
valuePtr = Marshal.SecureStringToGlobalAllocUnicode(secstring);
return Marshal.PtrToStringUni(valuePtr);
}
finally
{
Marshal.ZeroFreeGlobalAllocUnicode(valuePtr);
}
} // FromSecureString
// Binary source data
internal static byte[] ToByte(string Protected) {
if (Regex.IsMatch(Protected, "^A"))
return System.Convert.FromBase64String(Protected.Substring(1));
else if (Regex.IsMatch(Protected, "^D"))
return System.Convert.FromBase64String(Protected.Substring(1).Split('?')[0]);
else
{
Console.WriteLine("ERROR: Does not appear to be a ProtectedString signature.");
return null;
}
} // end ToByte
} // ProtectedString
private struct AESKeyConfig
{
internal string Salt;
internal int Iterations;
internal HashAlgorithmName Algorithm;
internal AESKeyConfig(string salt, int iterations, HashAlgorithmName hash)
{
this.Salt = salt;
this.Iterations = iterations;
this.Algorithm = hash;
}
}; // AESKeyConfig
private string defaultSalt = @"fMK9w4HDtMO4d8Oa4pmAw6U+fknCqWvil4Q9w73DscOtw64=";
private string privateKey = @"<Kirgudoo-Bambarbia>+[dwapara/326]&tRump-NAZIpid0R&Zelia-deadf@shist";
private SecureString masterPassword;
private AESKeyConfig aeskey;
public Encryption()
{
this.masterPassword = ProtectedString.ToSecureString(this.privateKey);
this.aeskey = new AESKeyConfig(this.defaultSalt, 60000, HashAlgorithmName.SHA256);
} // end constructor
public Encryption(string privateKey)
{
if (!string.IsNullOrEmpty(privateKey)) this.privateKey = privateKey;
this.masterPassword = ProtectedString.ToSecureString(this.privateKey);
this.aeskey = new AESKeyConfig(this.defaultSalt, 60000, HashAlgorithmName.SHA256);
} // end constructor
public void Dispose() // experimental
{
this.masterPassword.Dispose();
this.privateKey = null;
this.aeskey = new AESKeyConfig(null, 0, HashAlgorithmName.SHA256);
GC.Collect();
GC.SuppressFinalize(this);
}
public string Protect(string InputString, string EncryptionType = "AES", DataProtectionScope Scope = DataProtectionScope.CurrentUser)
{
if (string.IsNullOrEmpty(InputString)) return null; // show warning
if (string.IsNullOrEmpty(EncryptionType) ||
!Regex.IsMatch(EncryptionType.ToUpper(), "^AES|^DPAPI")) EncryptionType = "AES";
byte[] AESKey = null;
if (EncryptionType == "AES") AESKey = ProtectedString.ToAESKey(this.masterPassword,false,Get_AESKeyConfig(true));
return ProtectedString.Protect(InputString, EncryptionType, Scope, AESKey);
} // Protect_String
public string UnProtect(string InputString)
{
if (string.IsNullOrEmpty(InputString)) return null;
if (!Regex.IsMatch(InputString, "^[AD]"))
{
throw new Exception("Input string could not be converted to a ProtectedString Object. Verify that it was produced by Protect.");
}
byte[] AESKey = null;
if (Regex.IsMatch(InputString, "^A")) AESKey = ProtectedString.ToAESKey(this.masterPassword,false,Get_AESKeyConfig(true));
return ProtectedString.UnProtect(InputString, AESKey);
} // UnProtect_String
public dynamic Get_AESKeyConfig(bool convert=false)
{
if (convert) return new Hashtable { { "Salt", this.aeskey.Salt }, { "Iterations", this.aeskey.Iterations }, { "Algorithm", this.aeskey.Algorithm } };
else return this.aeskey;
} //
public void Set_AESKeyConfig(string SaltString, byte[] SaltBytes, bool Reset, int Iterations, HashAlgorithmName Hash)
{
if (Reset || this.aeskey.Salt == default || this.aeskey.Iterations == default)
{
this.aeskey = new AESKeyConfig(this.defaultSalt, 60000, HashAlgorithmName.SHA256);
return;
}
if (!string.IsNullOrEmpty(SaltString) && SaltBytes.Length != 0)
throw new Exception("ERROR: SaltString and SaltBytes cannot be used together.");
if (!string.IsNullOrEmpty(SaltString))
{
if (System.Text.Encoding.UTF8.GetBytes(SaltString).Length < 16)
{
Console.WriteLine("WARNING: Salt must be at least 16 bytes in length. Reset to default.");
this.aeskey.Salt = this.defaultSalt;
}
else
{
this.aeskey.Salt = System.Convert.ToBase64String(System.Text.Encoding.UTF8.GetBytes(SaltString));
}
}
else if (SaltBytes.Length != 0) this.aeskey.Salt = System.Convert.ToBase64String(SaltBytes);
else throw new Exception("ERROR: Neither SaltString nor SaltBytes specified.");
if (Iterations > 1) this.aeskey.Iterations = Iterations;
if (Hash != null) this.aeskey.Algorithm = Hash;
} // Set_AESKeyConfig
public void Set_MasterPassword(string NewPassword) {
if (!string.IsNullOrEmpty(NewPassword))
{
//Confirm_RTconfig();
this.privateKey = NewPassword;
this.masterPassword = ProtectedString.ToSecureString(NewPassword);
}
else Console.WriteLine("Empty new password specified.");
} // END Set_MasterPassword
public void Get_MasterPassword() {
//Confirm_RTconfig();
ProtectedString.FromSecureString(this.masterPassword);
}// END Get_MasterPassword
private void Confirm_RTconfig() // experimental
{
if (string.IsNullOrEmpty(this.defaultSalt)) this.defaultSalt = @"fMK9w4HDtMO4d8Oa4pmAw6U+fknCqWvil4Q9w73DscOtw64=";
if (string.IsNullOrEmpty(this.privateKey)) this.privateKey = @"<Kirgudoo-Bambarbia>+[dwapara/326]&tRump-NAZIpid0R&Zelia-deadf@shist";
if (this.masterPassword.Length == 0) ProtectedString.ToSecureString(this.privateKey);
if (string.IsNullOrEmpty(this.aeskey.Salt) && this.aeskey.Iterations == default) this.aeskey = new AESKeyConfig(this.defaultSalt, 60000, HashAlgorithmName.SHA256);
} // Confirm_RTconfig
} // Encryption class
} // Encryption namespace
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment