假设我需要在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加密/解密的类似帖子: Using AES encryption in C#
更新
I have reviewed the link Keith posted,但我还面临其他未知数。 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的byte []。阅读,我发现我可能需要使用相同的IV。由于ConvertFrom-SecureString和ConvertTo-SecureString能够使用简单的密钥加密/解密,因此我假设IV []可以是随机的 - 或者 - 具有静态定义。
我还没有找到一个成功的组合,但我会继续努力。
答案 0 :(得分:10)
我知道这是一个老帖子。我发布这个是为了完整性和后代,因为我无法在MSDN或stackoverflow上找到完整的答案。如果我需要再次这样做,它就会在这里。
它是使用AES加密的powershell< ConvertTo-SecureString的C#实现(使用-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);
}
答案 1 :(得分:3)
根据ConvertFrom-SecureString上的文档,使用AES加密算法:
如果使用Key或SecureKey指定加密密钥 参数,高级加密标准(AES)加密 使用算法。指定的密钥长度必须为128,192, 或256位,因为它们是AES支持的密钥长度 加密演算法。如果未指定密钥,则为Windows数据 Protection API(DPAPI)用于加密标准字符串 表示。
查看MSDN docs中的DecryptStringFromBytes_Aes示例。
BTW一个简单的选择是使用C#中的PowerShell引擎执行ConvertTo-SecureString
cmdlet来完成工作。否则,看起来初始化向量嵌入在ConvertFrom-SecureString输出中的某处,可能很容易提取,也可能不容易提取。
答案 2 :(得分:1)
如何在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";
如果您想获得解密字符,请检查方法中的数据。
答案 3 :(得分:0)
我发现最简单最简单的方法是直接从C#调用UISearchController
PowerShell命令。这样,在实现方面没有区别,输出正是您直接从PowerShell调用它的原因。
ConvertTo-SecureString
答案 4 :(得分:0)
添加到Cheng的答案-我发现我必须更改:
byte[] magicHeader = HexStringToByteArray(encrypted.Substring(0, 32));
到
byte[] magicHeader = HexStringToByteArray(psProtectedString.Substring(0, 32));
和
SymmetricAlgorithm algorithm = SymmetricAlgorithm.Create();
到
SymmetricAlgorithm algorithm = Aes.Create();
但是在其他方面效果很好。