In my opinion, extension methods are the most comfortable way to solve this.
I took Steve in CO's excellent answer and 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:
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;
}
}
With this, you can now simply convert your strings back and forth like so:
// create a secure string
System.Security.SecureString securePassword = "MyCleverPwd123".ToSecureString();
// convert it back to plain text (normal string)
String plainPassword = securePassword.ToPlainString();
But keep in mind the decoding method should only be used for testing.
If you're interested in the details: NetworkCredential
uses internally
using System.Runtime.InteropServices;
using System.Security;
private string MarshalToString(SecureString sstr)
{
if (sstr == null || sstr.Length == 0)
{
return string.Empty;
}
IntPtr intPtr = IntPtr.Zero;
string empty = string.Empty;
try
{
intPtr = Marshal.SecureStringToGlobalAllocUnicode(sstr);
return Marshal.PtrToStringUni(intPtr);
}
finally
{
if (intPtr != IntPtr.Zero)
{
Marshal.ZeroFreeGlobalAllocUnicode(intPtr);
}
}
}
private unsafe SecureString MarshalToSecureString(string str)
{
if (string.IsNullOrEmpty(str))
{
return new SecureString();
}
fixed (char* ptr = str)
{
char* value = ptr;
return new SecureString(value, str.Length);
}
}
to convert a SecureString
into a string
and vice versa.
And SecureString
internally uses
using System.Runtime.InteropServices;
[DllImport("crypt32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
internal static extern bool CryptProtectMemory(SafeBuffer pData, uint cbData, uint dwFlags);
[DllImport("crypt32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
internal static extern bool CryptUnprotectMemory(SafeBuffer pData, uint cbData, uint dwFlags);
to encrypt / decrypt the string data (pData
buffer) in situ (i.e. in place, without copying it). This means, you don't know how a SecureString is being internally encrypted. But since you can decrypt it with zero effort, it isn't really secure - it just prevents someone looking into a memory dump from finding plain text strings too easily. In other words, if you don't find a string in a memory dump easily, you obviously can't decrypt it (security through obscurity).