Created
April 19, 2026 18:13
-
-
Save scriptingstudio/fb7486dda1f82c3b7ef8dc5021c88b47 to your computer and use it in GitHub Desktop.
Experimental C# string encryptor
This file contains hidden or 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
| 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