How to use ConvertTo-SecureString

Let's say I need to do this in Powershell:

$SecurePass = Get-Content $CredPath | ConvertTo-SecureString -Key (1..16) [String]$CleartextPass = [Runtime.InteropServices.Marshal]::PtrToStringAuto([Runtime.InteropServices.Marshal]::SecureStringToBSTR($CredPass)); 

The contents of $ CredPath is a file containing the output of ConvertFrom-SecureString -Key (1..16).

How to execute the ConvertTo-SecureString -key (1..16) part in C # /. NET?

I know how to create a SecureString , but I'm not sure how encryption should be handled.

Do I encrypt each character with AES or decrypt a string and then create a secure string for the character?

I know almost nothing about cryptography, but from what I put together, I could just call the Powershell command using C #.

For reference, I found a similar AES encryption / decryption entry here: Using AES encryption in C #

UPDATE

I looked at the link posted by Keith , but I come across additional unknowns. DecryptStringFromBytes_Aes takes three arguments:

 static string DecryptStringFromBytes_Aes(byte[] cipherText, byte[] Key, byte[] IV) 

The first argument is a byte array representing the ciphertext. The question is, how should a string be displayed in an array of bytes? Should it be submitted with or without encoding?

 byte[] ciphertext = Encoding.ASCII.GetBytes(encrypted_text); byte[] ciphertext = Encoding.UTF8.GetBytes(encrypted_text); byte[] ciphertext = Encoding.Unicode.GetBytes(encrypted_text); byte[] ciphertext = new byte[encrypted_password.Length * sizeof(char)]; System.Buffer.BlockCopy(encrypted_password.ToCharArray(), 0, text, 0, text.Length); 

The second byte array is the key, which should be just an integer:

 byte[] key = { 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16 }; 

The third byte array is the “Initialization Vector” - it seems that calling Aes.Create () will generate byte [] for IV randomly. Looking through, I found that I might need to use the same IV. Since ConvertFrom-SecureString and ConvertTo-SecureString can encrypt / decrypt using only the key, I remain with the assumption that IV [] can be random or has a static definition.

I have not found a winning combination, but I will continue to try.

+7
source share
4 answers

I know this is an old post. I publish this for completeness and posterity because I could not find the full answer on MSDN or stackoverflow. It will be here if I ever need to do it again.

This is a C # implementation of powershell ConvertTo-SecureString with AES encryption (enabled with the -key option). I will leave it for exercises to code the C # ConvertFrom-SecureString implementation.

 # forward direction [securestring] $someSecureString = read-host -assecurestring [string] $psProtectedString = ConvertFrom-SecureString -key (1..16) -SecureString $someSecureString # reverse direction $back = ConvertTo-SecureString -string $psProtectedString -key (1..16) 

My work combines the answers and the reorganization of user2748365 so that they are more readable and add educational comments! I also fixed the problem with a substring - during this post, its code has only two elements in strArray.

 using System.IO; using System.Text; using System.Runtime.InteropServices; using System.Security; using System.Security.Cryptography; using System.Globalization; // psProtectedString - this is the output from // powershell> $psProtectedString = ConvertFrom-SecureString -SecureString $aSecureString -key (1..16) // key - make sure you add size checking // notes: this will throw an cryptographic invalid padding exception if it cannot decrypt correctly (wrong key) public static SecureString ConvertToSecureString(string psProtectedString, byte[] key) { // '|' is indeed the separater byte[] asBytes = Convert.FromBase64String( psProtectedString ); string[] strArray = Encoding.Unicode.GetString(asBytes).Split(new[] { '|' }); if (strArray.Length != 3) throw new InvalidDataException("input had incorrect format"); // strArray[0] is a static/magic header or signature (different passwords produce // the same header) It unused in our case, looks like 16 bytes as hex-string // you know strArray[1] is a base64 string by the '=' at the end // the IV is shorter than the body, and you can verify that it is the IV, // because it is exactly 16bytes=128bits and it decrypts the password correctly // you know strArray[2] is a hex-string because it is [0-9a-f] byte[] magicHeader = HexStringToByteArray(encrypted.Substring(0, 32)); byte[] rgbIV = Convert.FromBase64String(strArray[1]); byte[] cipherBytes = HexStringToByteArray(strArray[2]); // setup the decrypter SecureString str = new SecureString(); SymmetricAlgorithm algorithm = SymmetricAlgorithm.Create(); ICryptoTransform transform = algorithm.CreateDecryptor(key, rgbIV); using (var stream = new CryptoStream(new MemoryStream(cipherBytes), transform, CryptoStreamMode.Read)) { // using this silly loop format to loop one char at a time // so we never store the entire password naked in memory int numRed = 0; byte[] buffer = new byte[2]; // two bytes per unicode char while( (numRed = stream.Read(buffer, 0, buffer.Length)) > 0 ) { str.AppendChar(Encoding.Unicode.GetString(buffer).ToCharArray()[0]); } } // // non-production code // recover the SecureString; just to check // from http://stackoverflow.com/questions/818704/how-to-convert-securestring-to-system-string // IntPtr valuePtr = IntPtr.Zero; string secureStringValue = ""; try { // get the string back valuePtr = Marshal.SecureStringToGlobalAllocUnicode(str); secureStringValue = Marshal.PtrToStringUni(valuePtr); } finally { Marshal.ZeroFreeGlobalAllocUnicode(valuePtr); } return str; } // from http://stackoverflow.com/questions/311165/how-do-you-convert-byte-array-to-hexadecimal-string-and-vice-versa public static byte[] HexStringToByteArray(String hex) { int NumberChars = hex.Length; byte[] bytes = new byte[NumberChars / 2]; for (int i = 0; i < NumberChars; i += 2) bytes[i / 2] = Convert.ToByte(hex.Substring(i, 2), 16); return bytes; } public static SecureString DecryptPassword( string psPasswordFile, byte[] key ) { if( ! File.Exists(psPasswordFile)) throw new ArgumentException("file does not exist: " + psPasswordFile); string formattedCipherText = File.ReadAllText( psPasswordFile ); return ConvertToSecureString(formattedCipherText, key); } 
+8
source

According to the ConvertFrom-SecureString docs, the AES encryption algorithm is used:

If the encryption key is specified using the key or SecureKey parameters, the encryption standard is Advanced Encryption (AES) algorithm. The specified key must be 128, 192, or 256 bits long, because these are key lengths supported by the AES encryption algorithm. If the key is not specified, the Windows Security Data API (DPAPI) is used to encrypt the standard string representation.

See the DecryptStringFromBytes_Aes example in MSDN docs .

BTW, a simple option would be to use the PowerShell engine from C # to run the ConvertTo-SecureString cmdlet to do the job. Otherwise, it looks like the initialization vector is embedded somewhere in the ConvertFrom-SecureString output and may or may not be easily retrieved.

+3
source

How to execute the ConvertTo-SecureString -key (1..16) part in C # /. NET?

See the following code:

  private static SecureString ConvertToSecureString(string encrypted, string header, byte[] key) { string[] strArray = Encoding.Unicode.GetString(Convert.FromBase64String(encrypted.Substring(header.Length, encrypted.Length - header.Length))).Split(new[] {'|'}); SymmetricAlgorithm algorithm = SymmetricAlgorithm.Create(); int num2 = strArray[2].Length/2; var bytes = new byte[num2]; for (int i = 0; i < num2; i++) bytes[i] = byte.Parse(strArray[2].Substring(2*i, 2), NumberStyles.AllowHexSpecifier, CultureInfo.InvariantCulture); ICryptoTransform transform = algorithm.CreateDecryptor(key, Convert.FromBase64String(strArray[1])); using (var stream = new CryptoStream(new MemoryStream(bytes), transform, CryptoStreamMode.Read)) { var buffer = new byte[bytes.Length]; int num = stream.Read(buffer, 0, buffer.Length); var data = new byte[num]; for (int i = 0; i < num; i++) data[i] = buffer[i]; var str = new SecureString(); for (int j = 0; j < data.Length/2; j++) str.AppendChar((char) ((data[(2*j) + 1]*0x100) + data[2*j])); return str; } } 

Example:

  encrypted = "76492d1116743f0423413b16050a5345MgB8ADcAbgBiAGoAVQBCAFIANABNADgAYwBSAEoAQQA1AGQAZgAvAHYAYwAvAHcAPQA9AHwAZAAzADQAYwBhADYAOQAxAGIAZgA2ADgAZgA0AGMANwBjADQAYwBiADkAZgA1ADgAZgBiAGQAMwA3AGQAZgAzAA=="; header = "76492d1116743f0423413b16050a5345"; 

If you want to get decrypted characters, check the data in the method.

+1
source

It seemed to me that the easiest and easiest way is to call the ConvertTo-SecureString PowerShell command directly from C #. Thus, there is no difference in implementation, and the conclusion is exactly what it would be if you called it directly from PowerShell.

  string encryptedPassword = RunPowerShellCommand("\"" + password + "\" | ConvertTo-SecureString -AsPlainText -Force | ConvertFrom-SecureString", null); public static string RunPowerShellCommand(string command, Dictionary<string, object> parameters) { using (PowerShell powerShellInstance = PowerShell.Create()) { // Set up the running of the script powerShellInstance.AddScript(command); // Add the parameters if (parameters != null) { foreach (var parameter in parameters) { powerShellInstance.AddParameter(parameter.Key, parameter.Value); } } // Run the command Collection<PSObject> psOutput = powerShellInstance.Invoke(); StringBuilder stringBuilder = new StringBuilder(); if (powerShellInstance.Streams.Error.Count > 0) { foreach (var errorMessage in powerShellInstance.Streams.Error) { if (errorMessage != null) { throw new InvalidOperationException(errorMessage.ToString()); } } } foreach (var outputLine in psOutput) { if (outputLine != null) { stringBuilder.Append(outputLine); } } return stringBuilder.ToString(); } } 
0
source

All Articles