我如何使用ConvertTo-SecureString

假设我需要在Powershell中执行此操作:

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

$ CredPath的内容是一个包含ConvertFrom-SecureString -Key(1..16)输出的文件。

如何在C#/ .NET中完成ConvertTo-SecureString -key (1..16)部分?

我知道如何创建SecureString ,但我不确定应该如何处理加密。

我是否使用AES加密每个字符,或者解密字符串然后为每个字符创建一个安全字符串?

我对密码学几乎一无所知,但从我收集的内容中我可能只想用C#调用Powershell命令。

作为参考,我在这里发现了一篇关于AES加密/解密的类似post: 在C#中使用AES加密

UPDATE

我已经回顾了Keith发布的链接 ,但我还面临其他未知数。 DecryptStringFromBytes_Aes有三个参数:

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

第一个参数是字节数组表示加密文本。 这里的问题是,如何在字节数组中表示字符串? 它应该用或不用编码来表示?

 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); 

第二个字节数组的键应该只是一个整数数组:

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

第三个字节数组是“初始化向量” – 它看起来像Aes.Create()调用将随机生成一个字节[]。 阅读,我发现我可能需要使用相同的IV。 由于ConvertFrom-SecureString和ConvertTo-SecureString能够使用简单的密钥加密/解密,因此我假设IV []可以是随机的 – 或者 – 具有静态定义。

我还没有找到一个成功的组合,但我会继续努力。

我知道这是一个老post。 我发布这个是为了完整性和后代,因为我无法在MSDN或stackoverflow上找到完整的答案。 它会在这里,以防我再次需要这样做。

它是powershell的ConvertTo-SecureString的C#实现,带有AES加密(使用-key选项打开)。 我将把它留给练习来编写ConvertFrom-SecureString的C#实现。

 # 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) 

我的工作是结合答案并重新安排user2748365的答案,使其更具可读性并添加教育评论! 我还解决了使用子字符串的问题 – 在这篇文章发表时,他的代码在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); } 

根据ConvertFrom-SecureString上的文档,使用AES加密算法:

如果使用Key或SecureKey参数指定加密密钥,则使用高级加密标准(AES)加密算法。 指定密钥的长度必须为128,192或256位,因为这些是AES加密算法支持的密钥长度。 如果未指定密钥,则使用Windows Data Protection API(DPAPI)加密标准字符串表示forms。

查看MSDN文档中的DecryptStringFromBytes_Aes示例。

BTW一个简单的选择是使用C#中的PowerShell引擎来执行ConvertTo-SecureString cmdlet来完成工作。 否则,看起来初始化向量嵌入在ConvertFrom-SecureString输出中的某处,可能也可能不容易提取。

如何在C#/ .NET中完成ConvertTo-SecureString -key(1..16)部分?

请参阅以下代码:

  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; } } 

例:

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

如果您想获得解密字符,请检查方法中的数据

我发现最简单最简单的方法是直接从C#调用ConvertTo-SecureString PowerShell命令。 这样,在实现方面没有区别,输出就是直接从PowerShell调用它的结果。

  string encryptedPassword = RunPowerShellCommand("\"" + password + "\" | ConvertTo-SecureString -AsPlainText -Force | ConvertFrom-SecureString", null); public static string RunPowerShellCommand(string command, Dictionary 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 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(); } }