How to convert SecureString to System.String?

Multi tool use
Multi tool use


How to convert SecureString to System.String?



All reservations about unsecuring your SecureString by creating a System.String out of it aside, how can it be done?



How can I convert an ordinary System.Security.SecureString to 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 not trying to use that to increase my security.



I'm aware of Marshal.SecureStringToBSTR, but I don't know how to take that BSTR and make a System.String out of it.



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.




7 Answers
7



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





Got my up-vote too even years later, thanks for the help! Just a quick note: this also works as a static, in its own memory.
– John Suit
Sep 17 '14 at 15:18





I used StopWatch and SecureStringToString took 4.6sec to run. It's to slow for me. Does anyone get the same time or something faster?
– radbyx
Jan 6 '16 at 11:44


StopWatch


SecureStringToString





@radbyx In a quick and dirty test setup, I can call it 1000 times in 76ms. The first invocation takes 0.3 ms and subsequent invocations ~0.07ms. How large is your secure string and which version of the framework are you using?
– Rasmus Faber
Jan 6 '16 at 12:00





Length om my secureString is 168. I am using .NET Framework 3.5 if that answered your question? I have tryed 5-10 times is always around 4.5-4.65 sec~ I would love to get your time
– radbyx
Jan 6 '16 at 12:07





@RasmusFaber My bad, I had added a Database.GetConnectionString() into your code, to get my secureString, which was the evil part that took almost 5sec(and yes I should look into that! :) Your code took .00 mili seconds in my stopwatch so it's all good. Thanks for pointing me in the right direction.
– radbyx
Jan 6 '16 at 12:20



Database.GetConnectionString()



Obviously you know how this defeats the whole purpose of a SecureString, but I'll restate it anyway.



If you want a one-liner, try this: (.NET 4 and above only)


string password = new System.Net.NetworkCredential(string.Empty, securePassword).Password;



Where securePassword is a SecureString.





Although it does defeat the purpose in production, your solution is perfect for unit tests. Thanks.
– beterthanlife
Dec 10 '14 at 12:18





This helped me to figure out that a SecureString (System.Security.SecureString) was not being passed to my ApiController (webapi). Thx
– granadaCoder
Jan 6 '16 at 17:54





Note in PowerShell this is [System.Net.NetworkCredential]::new('', $securePassword).Password
– stijn
Jul 17 '17 at 14:12



[System.Net.NetworkCredential]::new('', $securePassword).Password



Dang. right after 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. :)


static String SecureStringToString(SecureString value)
{
IntPtr bstr = Marshal.SecureStringToBSTR(value);

try
{
return Marshal.PtrToStringBSTR(bstr);
}
finally
{
Marshal.FreeBSTR(bstr);
}
}





You can certainly use the unsafe keyword and a char*, just call bstr.ToPointer() and cast.
– Ben Voigt
Jun 21 '16 at 16:23


unsafe


char*


bstr.ToPointer()



I think it would be best for SecureString dependent functions to encapsulate their 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:


finally



This obviously makes it a lot easier to "standardize" and maintain callers vs. relying on less desirable alternatives:


string DecryptSecureString(...)



Notice here, you have two options:


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


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
{
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


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



Obviously, this doesn't prevent abuse of this function in the following manner, so just be careful not to do this:


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



Happy coding!



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
String plainPassword = securePassword.ToPlainString(); // convert back to normal string



But keep in mind the decoding method should only be used for testing.


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





This answer has a memory leak.
– Ben Voigt
Sep 8 '15 at 22:15





@BenVoigt Can you explain further please how this has a memory leak?
– El Ronnoco
Jun 21 '16 at 15:47





@ElRonnoco: Nothing frees the BSTR explicitly, and it's not a .NET object so the garbage collector doesn't take care of it either. Compare to stackoverflow.com/a/818709/103167 which was posted 5 years earlier and doesn't leak.
– Ben Voigt
Jun 21 '16 at 16:18



BSTR





@BenVoigt Thanks for the response! :)
– El Ronnoco
Jun 22 '16 at 9:32



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.


StringBuilder


string


StringBuilder.Append(plainTextPassword);
StringBuilder.Clear();
// overwrite with reasonably random characters
StringBuilder.Append(New Guid().ToString());





While this is true, the garbage collector may still move the StringBuilder buffer around in memory during generational compaction, which makes the "overwrite the actual value" fail, because there is another (or more) leftover copy that isn't destroyed.
– Ben Voigt
Jun 21 '16 at 16:20





This doesn't even remotely answer the question.
– Jay Sullivan
Aug 10 '16 at 2:50






By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

UT,sq Trk sqp,eUMDGOrJ1wHX,XxaHsLCzzT2QzqcF1RSi pOV84 4pf,FGH,ff X15
Sexo kGNC SD1XbpiYyWmzf1hQot0n,kx0loQr GD leD4PjW9 KlrjpbsBMz 7

Popular posts from this blog

PHP contact form sending but not receiving emails

Do graphics cards have individual ID by which single devices can be distinguished?

Create weekly swift ios local notifications