C# 如何将 SecureString 转换为 System.String?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/818704/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to convert SecureString to System.String?
提问by Andrew Arnott
All reservations about unsecuring your SecureString by creating a System.String out of it aside, how can it be done?
有关取消保护通过创建它以System.String你的SecureString的所有预订一边,怎么能做到呢?
How can I convert an ordinary System.Security.SecureString to System.String?
如何将普通的 System.Security.SecureString 转换为 System.String?
I'm sure many of you who are familiar with SecureString are going to respond that one should never transform a SecureString to an ordinary .NET string because it removes all security protections. I know. But right now my program does everything with ordinary strings anyway, and I'm trying to enhance its security and although I'm going to be using an API that returns a SecureString to me I am nottrying to use that to increase my security.
我相信很多熟悉 SecureString 的人都会回答说永远不要将 SecureString 转换为普通的 .NET 字符串,因为它会删除所有安全保护。 我知道。但是现在我的程序无论如何都用普通字符串来做所有事情,我正在努力提高它的安全性,虽然我将使用一个向我返回 SecureString 的 API,但我并没有试图使用它来提高我的安全性。
I'm aware of Marshal.SecureStringToBSTR, but I don't know how to take that BSTR and make a System.String out of it.
我知道 Marshal.SecureStringToBSTR,但我不知道如何使用该 BSTR 并从中制作 System.String。
For those who may demand to know why I would ever want to do this, well, I'm taking a password from a user and submitting it as an html form POST to log the user into a web site. So... this really has to be done with managed, unencrypted buffers. If I could even get access to the unmanaged, unencrypted buffer I imagine I could do byte-by-byte stream writing on the network stream and hope that that keeps the password secure the whole way. I'm hoping for an answer to at least one of these scenarios.
对于那些可能需要知道我为什么要这样做的人,好吧,我从用户那里获取密码并将其作为 html 表单 POST 提交,以将用户登录到网站。所以......这真的必须用托管的、未加密的缓冲区来完成。如果我什至可以访问非托管、未加密的缓冲区,我想我可以在网络流上逐字节地写入流,并希望这样可以保证密码的安全。我希望至少对其中一种情况有答案。
采纳答案by Rasmus Faber
Use the System.Runtime.InteropServices.Marshal
class:
使用System.Runtime.InteropServices.Marshal
类:
String SecureStringToString(SecureString value) {
IntPtr valuePtr = IntPtr.Zero;
try {
valuePtr = Marshal.SecureStringToGlobalAllocUnicode(value);
return Marshal.PtrToStringUni(valuePtr);
} finally {
Marshal.ZeroFreeGlobalAllocUnicode(valuePtr);
}
}
If you want to avoid creating a managed string object, you can access the raw data using Marshal.ReadInt16(IntPtr, Int32)
:
如果要避免创建托管字符串对象,可以使用以下方法访问原始数据Marshal.ReadInt16(IntPtr, Int32)
:
void HandleSecureString(SecureString value) {
IntPtr valuePtr = IntPtr.Zero;
try {
valuePtr = Marshal.SecureStringToGlobalAllocUnicode(value);
for (int i=0; i < value.Length; i++) {
short unicodeChar = Marshal.ReadInt16(valuePtr, i*2);
// handle unicodeChar
}
} finally {
Marshal.ZeroFreeGlobalAllocUnicode(valuePtr);
}
}
回答by Andrew Arnott
Dang. rightafter posting this I found the answer deep in this article. But if anyone knows how to access the IntPtr unmanaged, unencrypted buffer that this method exposes, one byte at a time so that I don't have to create a managed string object out of it to keep my security high, please add an answer. :)
党。 右张贴在此之后,我发现在回答深这篇文章。但是,如果有人知道如何访问此方法公开的 IntPtr 非托管、未加密缓冲区,一次一个字节,这样我就不必从中创建托管字符串对象来保持高安全性,请添加答案。:)
static String SecureStringToString(SecureString value)
{
IntPtr bstr = Marshal.SecureStringToBSTR(value);
try
{
return Marshal.PtrToStringBSTR(bstr);
}
finally
{
Marshal.FreeBSTR(bstr);
}
}
回答by Steve In CO
Obviously you know how this defeats the whole purpose of a SecureString, but I'll restate it anyway.
显然您知道这如何违背 SecureString 的全部目的,但我还是要重申一下。
If you want a one-liner, try this: (.NET 4 and above only)
如果你想要一个单线,试试这个:(仅限 .NET 4 及更高版本)
string password = new System.Net.NetworkCredential(string.Empty, securePassword).Password;
Where securePassword is a SecureString.
其中 securePassword 是 SecureString。
回答by Jesse Motes
// using so that Marshal doesn't have to be qualified
using System.Runtime.InteropServices;
//using for SecureString
using System.Security;
public string DecodeSecureString (SecureString Convert)
{
//convert to IntPtr using Marshal
IntPtr cvttmpst = Marshal.SecureStringToBSTR(Convert);
//convert to string using Marshal
string cvtPlainPassword = Marshal.PtrToStringAuto(cvttmpst);
//return the now plain string
return cvtPlainPassword;
}
回答by Matt Borja
I think it would be best for SecureString
dependent functions to encapsulatetheir dependent logic in an anonymous function for better control over the decrypted string in memory (once pinned).
我认为SecureString
依赖函数最好将它们的依赖逻辑封装在一个匿名函数中,以便更好地控制内存中的解密字符串(一旦固定)。
The implementation for decrypting SecureStrings in this snippet will:
此代码段中解密 SecureStrings 的实现将:
- Pin the string in memory (which is what you want to do but appears to be missing from most answers here).
- Pass its referenceto the Func/Action delegate.
- Scrub it from memory and release the GC in the
finally
block.
- 将字符串固定在内存中(这是您想要执行的操作,但此处的大多数答案似乎都没有)。
- 将其引用传递给 Func/Action 委托。
- 从内存中清理它并释放
finally
块中的 GC 。
This obviously makes it a lot easier to "standardize" and maintain callers vs. relying on less desirable alternatives:
与依赖不太理想的替代方案相比,这显然使“标准化”和维护调用者变得更加容易:
- Returning the decrypted string from a
string DecryptSecureString(...)
helper function. - Duplicating this code wherever it is needed.
- 从
string DecryptSecureString(...)
辅助函数返回解密后的字符串。 - 在需要的地方复制此代码。
Notice here, you have two options:
请注意,您有两个选择:
static T DecryptSecureString<T>
which allows you to access the result of theFunc
delegate from the caller (as shown in theDecryptSecureStringWithFunc
test method).static void DecryptSecureString
is simply a "void" version which employ anAction
delegate in cases where you actually don't want/need to return anything (as demonstrated in theDecryptSecureStringWithAction
test method).
static T DecryptSecureString<T>
它允许您Func
从调用者访问委托的结果(如DecryptSecureStringWithFunc
测试方法中所示)。static void DecryptSecureString
只是一个“无效”版本,它Action
在您实际上不想/不需要返回任何内容的情况下使用委托(如DecryptSecureStringWithAction
测试方法中所示)。
Example usage for both can be found in the StringsTest
class included.
可以在StringsTest
包含的类中找到两者的示例用法。
Strings.cs
字符串.cs
using System;
using System.Runtime.InteropServices;
using System.Security;
namespace SecurityUtils
{
public partial class Strings
{
/// <summary>
/// Passes decrypted password String pinned in memory to Func delegate scrubbed on return.
/// </summary>
/// <typeparam name="T">Generic type returned by Func delegate</typeparam>
/// <param name="action">Func delegate which will receive the decrypted password pinned in memory as a String object</param>
/// <returns>Result of Func delegate</returns>
public static T DecryptSecureString<T>(SecureString secureString, Func<string, T> action)
{
var insecureStringPointer = IntPtr.Zero;
var insecureString = String.Empty;
var gcHandler = GCHandle.Alloc(insecureString, GCHandleType.Pinned);
try
{
insecureStringPointer = Marshal.SecureStringToGlobalAllocUnicode(secureString);
insecureString = Marshal.PtrToStringUni(insecureStringPointer);
return action(insecureString);
}
finally
{
//clear memory immediately - don't wait for garbage collector
fixed(char* ptr = insecureString )
{
for(int i = 0; i < insecureString.Length; i++)
{
ptr[i] = 'using Microsoft.VisualStudio.TestTools.UnitTesting;
using System.Security;
namespace SecurityUtils.Test
{
[TestClass]
public class StringsTest
{
[TestMethod]
public void DecryptSecureStringWithFunc()
{
// Arrange
var secureString = new SecureString();
foreach (var c in "UserPassword123".ToCharArray())
secureString.AppendChar(c);
secureString.MakeReadOnly();
// Act
var result = Strings.DecryptSecureString<bool>(secureString, (password) =>
{
return password.Equals("UserPassword123");
});
// Assert
Assert.IsTrue(result);
}
[TestMethod]
public void DecryptSecureStringWithAction()
{
// Arrange
var secureString = new SecureString();
foreach (var c in "UserPassword123".ToCharArray())
secureString.AppendChar(c);
secureString.MakeReadOnly();
// Act
var result = false;
Strings.DecryptSecureString(secureString, (password) =>
{
result = password.Equals("UserPassword123");
});
// Assert
Assert.IsTrue(result);
}
}
}
';
}
}
insecureString = null;
gcHandler.Free();
Marshal.ZeroFreeGlobalAllocUnicode(insecureStringPointer);
}
}
/// <summary>
/// Runs DecryptSecureString with support for Action to leverage void return type
/// </summary>
/// <param name="secureString"></param>
/// <param name="action"></param>
public static void DecryptSecureString(SecureString secureString, Action<string> action)
{
DecryptSecureString<int>(secureString, (s) =>
{
action(s);
return 0;
});
}
}
}
StringsTest.cs
字符串测试.cs
[TestMethod]
public void DecryptSecureStringWithAction()
{
// Arrange
var secureString = new SecureString();
foreach (var c in "UserPassword123".ToCharArray())
secureString.AppendChar(c);
secureString.MakeReadOnly();
// Act
string copyPassword = null;
Strings.DecryptSecureString(secureString, (password) =>
{
copyPassword = password; // Please don't do this!
});
// Assert
Assert.IsNull(copyPassword); // Fails
}
Obviously, this doesn't prevent abuse of this function in the following manner, so just be careful not to do this:
显然,这并不能防止以以下方式滥用此功能,因此请注意不要这样做:
StringBuilder.Append(plainTextPassword);
StringBuilder.Clear();
// overwrite with reasonably random characters
StringBuilder.Append(New Guid().ToString());
Happy coding!
快乐编码!
回答by Michael Liben
If you use a StringBuilder
instead of a string
, you can overwrite the actual value in memory when you are done. That way the password won't hang around in memory until garbage collection picks it up.
如果使用 aStringBuilder
而不是 a string
,则可以在完成后覆盖内存中的实际值。这样密码就不会在内存中徘徊,直到垃圾收集收集它。
public static class Extensions
{
// convert a secure string into a normal plain text string
public static String ToPlainString(this System.Security.SecureString secureStr)
{
String plainStr=new System.Net.NetworkCredential(string.Empty, secureStr).Password;
return plainStr;
}
// convert a plain text string into a secure string
public static System.Security.SecureString ToSecureString(this String plainStr)
{
var secStr = new System.Security.SecureString(); secStr.Clear();
foreach (char c in plainStr.ToCharArray())
{
secStr.AppendChar(c);
}
return secStr;
}
}
回答by Matt
In my opinion, extension methodsare the most comfortable way to solve this.
在我看来,扩展方法是解决这个问题最舒服的方法。
I took Steve in CO'sexcellent answerand put it into an extension class as follows, together with a second method I added to support the other direction (string -> secure string) as well, so you can create a secure string and convert it into a normal string afterwards:
我把史蒂夫在 CO 的优秀答案中放入一个扩展类中,如下所示,以及我添加的第二种方法来支持另一个方向(字符串 - > 安全字符串),因此您可以创建一个安全字符串并将其转换为之后的正常字符串:
// create a secure string
System.Security.SecureString securePassword = "MyCleverPwd123".ToSecureString();
// convert it back to plain text
String plainPassword = securePassword.ToPlainString(); // convert back to normal string
With this, you can now simply convert your strings back and forthlike so:
有了这个,您现在可以像这样简单地来回转换字符串:
/// <summary>
/// Allows a decrypted secure string to be used whilst minimising the exposure of the
/// unencrypted string.
/// </summary>
/// <typeparam name="T">Generic type returned by Func delegate.</typeparam>
/// <param name="secureString">The string to decrypt.</param>
/// <param name="action">
/// Func delegate which will receive the decrypted password as a string object
/// </param>
/// <returns>Result of Func delegate</returns>
/// <remarks>
/// This method creates an empty managed string and pins it so that the garbage collector
/// cannot move it around and create copies. An unmanaged copy of the the secure string is
/// then created and copied into the managed string. The action is then called using the
/// managed string. Both the managed and unmanaged strings are then zeroed to erase their
/// contents. The managed string is unpinned so that the garbage collector can resume normal
/// behaviour and the unmanaged string is freed.
/// </remarks>
public static T UseDecryptedSecureString<T>(this SecureString secureString, Func<string, T> action)
{
int length = secureString.Length;
IntPtr sourceStringPointer = IntPtr.Zero;
// Create an empty string of the correct size and pin it so that the GC can't move it around.
string insecureString = new string('using System;
using System.Security;
using System.Runtime.InteropServices;
namespace SecureStringsEasy
{
public static class MyExtensions
{
public static SecureString ToSecureString(string input)
{
SecureString secureString = new SecureString();
foreach (var item in input)
{
secureString.AppendChar(item);
}
return secureString;
}
public static string ToNormalString(SecureString input)
{
IntPtr strptr = Marshal.SecureStringToBSTR(input);
string normal = Marshal.PtrToStringBSTR(strptr);
Marshal.ZeroFreeBSTR(strptr);
return normal;
}
}
}
', length);
var insecureStringHandler = GCHandle.Alloc(insecureString, GCHandleType.Pinned);
IntPtr insecureStringPointer = insecureStringHandler.AddrOfPinnedObject();
try
{
// Create an unmanaged copy of the secure string.
sourceStringPointer = Marshal.SecureStringToBSTR(secureString);
// Use the pointers to copy from the unmanaged to managed string.
for (int i = 0; i < secureString.Length; i++)
{
short unicodeChar = Marshal.ReadInt16(sourceStringPointer, i * 2);
Marshal.WriteInt16(insecureStringPointer, i * 2, unicodeChar);
}
return action(insecureString);
}
finally
{
// Zero the managed string so that the string is erased. Then unpin it to allow the
// GC to take over.
Marshal.Copy(new byte[length], 0, insecureStringPointer, length);
insecureStringHandler.Free();
// Zero and free the unmanaged string.
Marshal.ZeroFreeBSTR(sourceStringPointer);
}
}
/// <summary>
/// Allows a decrypted secure string to be used whilst minimising the exposure of the
/// unencrypted string.
/// </summary>
/// <param name="secureString">The string to decrypt.</param>
/// <param name="action">
/// Func delegate which will receive the decrypted password as a string object
/// </param>
/// <returns>Result of Func delegate</returns>
/// <remarks>
/// This method creates an empty managed string and pins it so that the garbage collector
/// cannot move it around and create copies. An unmanaged copy of the the secure string is
/// then created and copied into the managed string. The action is then called using the
/// managed string. Both the managed and unmanaged strings are then zeroed to erase their
/// contents. The managed string is unpinned so that the garbage collector can resume normal
/// behaviour and the unmanaged string is freed.
/// </remarks>
public static void UseDecryptedSecureString(this SecureString secureString, Action<string> action)
{
UseDecryptedSecureString(secureString, (s) =>
{
action(s);
return 0;
});
}
But keep in mind the decoding method should only be used for testing.
但请记住,解码方法只能用于测试。
回答by sclarke81
I created the following extension methods based on the answer from rdev5. Pinning the managed string is important as it prevents the garbage collector from moving it around and leaving behind copies that you're unable to erase.
我根据rdev5的答案创建了以下扩展方法。固定托管字符串很重要,因为它可以防止垃圾收集器四处移动它并留下您无法擦除的副本。
I think the advantage of my solution has is that no unsafe code is needed.
我认为我的解决方案的优点是不需要不安全的代码。
Marshal.Copy(new byte[length], 0, insecureStringPointer, length);
回答by Eric Alexander Silveira
This C# code is what you want.
此 C# 代码正是您想要的。
%ProjectPath%/SecureStringsEasy.cs
%ProjectPath%/SecureStringsEasy.cs
Marshal.Copy(new byte[length * 2], 0, insecureStringPointer, length * 2);
回答by John Flaherty
I derived from This answer by sclarke81. I like his answer and I'm using the derivative but sclarke81's has a bug. I don't have reputation so I can't comment. The problem seems small enough that it didn't warrant another answer and I could edit it. So I did. It got rejected. So now we have another answer.
我来自sclarke81 的这个答案。我喜欢他的回答,我正在使用衍生工具,但 sclarke81 有一个错误。我没有声誉,所以我无法发表评论。这个问题似乎很小,不需要另一个答案,我可以编辑它。所以我做了。它被拒绝了。所以现在我们有了另一个答案。
sclarke81 I hope you see this (in finally):
sclarke81 我希望你看到这个(最后):
///
/// Allows a decrypted secure string to be used whilst minimising the exposure of the
/// unencrypted string.
///
/// Generic type returned by Func delegate.
/// The string to decrypt.
///
/// Func delegate which will receive the decrypted password as a string object
///
/// Result of Func delegate
///
/// This method creates an empty managed string and pins it so that the garbage collector
/// cannot move it around and create copies. An unmanaged copy of the the secure string is
/// then created and copied into the managed string. The action is then called using the
/// managed string. Both the managed and unmanaged strings are then zeroed to erase their
/// contents. The managed string is unpinned so that the garbage collector can resume normal
/// behaviour and the unmanaged string is freed.
///
public static T UseDecryptedSecureString(this SecureString secureString, Func action)
{
int length = secureString.Length;
IntPtr sourceStringPointer = IntPtr.Zero;
// Create an empty string of the correct size and pin it so that the GC can't move it around.
string insecureString = new string('##代码##', length);
var insecureStringHandler = GCHandle.Alloc(insecureString, GCHandleType.Pinned);
IntPtr insecureStringPointer = insecureStringHandler.AddrOfPinnedObject();
try
{
// Create an unmanaged copy of the secure string.
sourceStringPointer = Marshal.SecureStringToBSTR(secureString);
// Use the pointers to copy from the unmanaged to managed string.
for (int i = 0; i < secureString.Length; i++)
{
short unicodeChar = Marshal.ReadInt16(sourceStringPointer, i * 2);
Marshal.WriteInt16(insecureStringPointer, i * 2, unicodeChar);
}
return action(insecureString);
}
finally
{
// Zero the managed string so that the string is erased. Then unpin it to allow the
// GC to take over.
Marshal.Copy(new byte[length * 2], 0, insecureStringPointer, length * 2);
insecureStringHandler.Free();
// Zero and free the unmanaged string.
Marshal.ZeroFreeBSTR(sourceStringPointer);
}
}
///
/// Allows a decrypted secure string to be used whilst minimising the exposure of the
/// unencrypted string.
///
/// The string to decrypt.
///
/// Func delegate which will receive the decrypted password as a string object
///
/// Result of Func delegate
///
/// This method creates an empty managed string and pins it so that the garbage collector
/// cannot move it around and create copies. An unmanaged copy of the the secure string is
/// then created and copied into the managed string. The action is then called using the
/// managed string. Both the managed and unmanaged strings are then zeroed to erase their
/// contents. The managed string is unpinned so that the garbage collector can resume normal
/// behaviour and the unmanaged string is freed.
///
public static void UseDecryptedSecureString(this SecureString secureString, Action action)
{
UseDecryptedSecureString(secureString, (s) =>
{
action(s);
return 0;
});
}
}
should be:
应该:
##代码##And the full answer with the bug fix:
以及错误修复的完整答案:
##代码##