How to convert SecureString to System.String?

147,309

Solution 1

Use the System.Runtime.InteropServices.Marshal class:

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

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

Solution 2

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.

Solution 3

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

Solution 4

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.

Solution 5

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

The implementation for decrypting SecureStrings in this snippet will:

  1. Pin the string in memory (which is what you want to do but appears to be missing from most answers here).
  2. Pass its reference to the Func/Action delegate.
  3. Scrub it from memory and release the GC in the finally block.

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.

Notice here, you have two options:

  1. static T DecryptSecureString<T> which allows you to access the result of the Func delegate from the caller (as shown in the DecryptSecureStringWithFunc test method).
  2. static void DecryptSecureString is simply a "void" version which employ an Action delegate in cases where you actually don't want/need to return anything (as demonstrated in the DecryptSecureStringWithAction test method).

Example usage for both can be found in the StringsTest class included.

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
            {
                //clear memory immediately - don't wait for garbage collector
                fixed(char* ptr = insecureString )
                {
                    for(int i = 0; i < insecureString.Length; i++)
                    {
                        ptr[i] = '\0';
                    }
                }

                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!

Share:
147,309

Related videos on Youtube

Andrew Arnott
Author by

Andrew Arnott

Loving all things .NET: C#, ASP.NET, WCF, WPF. Loving all things identity: OpenID, OAuth, Cardspace, X.509. Appreciate my answers? Please donate bitcoins to 15j5QJNfvHkWaEFJBDQjVjoA2828mQE7Wr

Updated on February 19, 2022

Comments

  • Andrew Arnott
    Andrew Arnott about 2 years

    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.

  • John Suit
    John Suit over 9 years
    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.
  • beterthanlife
    beterthanlife over 9 years
    Although it does defeat the purpose in production, your solution is perfect for unit tests. Thanks.
  • Ben Voigt
    Ben Voigt over 8 years
    This answer has a memory leak.
  • radbyx
    radbyx over 8 years
    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?
  • Rasmus Faber
    Rasmus Faber over 8 years
    @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?
  • radbyx
    radbyx over 8 years
    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
    radbyx over 8 years
    @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.
  • granadaCoder
    granadaCoder over 8 years
    This helped me to figure out that a SecureString (System.Security.SecureString) was not being passed to my ApiController (webapi). Thx
  • El Ronnoco
    El Ronnoco almost 8 years
    @BenVoigt Can you explain further please how this has a memory leak?
  • Ben Voigt
    Ben Voigt almost 8 years
    @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
    Ben Voigt almost 8 years
    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
    Ben Voigt almost 8 years
    You can certainly use the unsafe keyword and a char*, just call bstr.ToPointer() and cast.
  • Jay Sullivan
    Jay Sullivan almost 8 years
    This doesn't even remotely answer the question.
  • jp2code
    jp2code over 7 years
    I used the exact code in this answer. Every time I pass in a SecureString value, Marshal.PtrToStringUni returns an Empty String. Any idea why or how to find out what is going wrong?
  • stijn
    stijn almost 7 years
    Note in PowerShell this is [System.Net.NetworkCredential]::new('', $securePassword).Password
  • Dai
    Dai over 5 years
    The code should use SecureStringToBSTR because SecureString can contain \0 as non-terminating characters, but SecureStringToGlobalAllocUnicode treat it as a null-terminated string.
  • Maximilian Burszley
    Maximilian Burszley over 5 years
    @stijn Just being pedantic, but more accurately: [System.Net.Credential]::new([string]::Empty, $securePassword).Password. Even so, that requires version 5 to work: (New-Object Net.Credential [string]::Empty, $securePassword).Password
  • stijn
    stijn over 5 years
    @TheIncorrigible1 can you elaborate? E.g. when is '' not the same type as [String]::Empty? Also New-Object Net.Credential doesn't work for me: Cannot find type [Net.Credential]: verify that the assembly containing this type is loaded
  • sclarke81
    sclarke81 over 5 years
    Why not use Marshal.Copy(new byte[insecureString.Length], 0, insecureStringPointer, (int)insecureString.Length); instead of the fixed section?
  • Jins Peter
    Jins Peter almost 5 years
    why does this defeat the purpose? I don't understand.?
  • Steve In CO
    Steve In CO over 4 years
    It defeats the purpose of a SecureString because it makes a non-encrypted copy of your SecureString content in to a normal string. Every time you do that, you are adding at least one (and with Garbage Collection possibly multiple) copies of your unencrypted string to memory. This is considered a risk for some security sensitive applications and SecureString was implemented specifically to reduce the risk.
  • Wim Coenen
    Wim Coenen over 4 years
    @BenVoigt BSTR has a null terminator after the string data for safety, but also allows null characters embedded in the string. So it's a bit more complicated than that, you also need to retrieve the length prefix that sits before that pointer. docs.microsoft.com/en-us/previous-versions/windows/desktop/…
  • Ben Voigt
    Ben Voigt over 4 years
    @WimCoenen: True but unimportant. The length stored in the BSTR will be a copy of the length already available from SecureString.Length.
  • Wim Coenen
    Wim Coenen over 4 years
    @BenVoigt ah, my bad. I thought SecureString didn't expose any information about the string.
  • Ben Voigt
    Ben Voigt over 4 years
    @WimCoenen: SecureString is not trying to hide the value, it's trying to prevent copies of the value from being made into regions which cannot be reliably overwritten, such as garbage collected memory, pagefile, etc. The intention is that when the SecureString lifetime ends, absolutely no copy of the secret remains in memory. It doesn't prevent you from making and leaking a copy, but it never does.
  • Ben Voigt
    Ben Voigt over 4 years
    While your code doesn't leak a copy of the string, it still represents a pit of despair. Nearly every operation on the System.String object will make unpinned and unerased copies. That's why this isn't built into SecureString.
  • mklement0
    mklement0 about 4 years
    It's somewhat of a moot point, given that you're creating a managed copy, but it's generally better to call Marshal.ZeroFreeBSTR(), not Marshal.FreeBSTR().
  • mklement0
    mklement0 about 4 years
    @sclarke81, good idea, but you'll need to use [char], not [byte].
  • mklement0
    mklement0 about 4 years
    Nice, though to zero out the entire string you'll have to use new char[length] (or multiply length with sizeof(char)).
  • mklement0
    mklement0 about 4 years
    @BenVoigt: As long as the action delegate doesn't create copies of the temporary, pinned, then zeroed-out string, this approach should be as safe as or unsafe as SecureString itself - to use the latter, a plain-text representation too has to be created at some point, given that secure strings aren't OS-level constructs; the relative security comes from controlling the lifetime of that string and ensuring that it gets erased after use.
  • mklement0
    mklement0 about 4 years
    The overall approach is promising, but I don't think your attempt at pinning the managed string that contains the insecure (plain-text) copy is effective: what you're pinning instead is the original string object that you've initialized to String.Empty, not the newly allocated instance created and returned by Marshal.PtrToStringUni().
  • mklement0
    mklement0 about 4 years
    Good point; I've left a comment on the referenced answer, which should notify the OP.
  • Ben Voigt
    Ben Voigt about 4 years
    @mklement0: SecureString doesn't have member functions and overloaded operators that make copies all over the place. System.String does.
  • mklement0
    mklement0 about 4 years
    @BenVoigt, yes, it's very easy to create accidental copies of managed strings; as as long as the action delegate doesn't do that - and that's what this hinges on - this approach should be fine; passing the string through as-is to some API that requires a plain-text password fulfills that requirement (leaving aside the issue of how careful that API is in controlling the string).
  • Ben Voigt
    Ben Voigt about 4 years
    @mklement0: But "passing the string through as-is to some API" can be done using the SecureString object -- p/invoke knows what to do with it. The problem is in using it like it's an ordinary managed string, because you made it look like one. Basically anything that needs to be done will, by default, get done wrong with this call signature, whether that be converting to an encoding other than UCS-2, trimming or appending data, escaping control characters, or whatever. Even calling a string comparison function could put you at risk if that function computes a normalized version!
  • mklement0
    mklement0 about 4 years
    @BenVoigt, not all APIs support SecureString - e.g., System.DirectoryServices.AccountManagement.PrincipalContext.‌​ValidateCredentials(‌​) - and that's what this approach is for - undoubtedly, you have to be careful.
  • Ben Voigt
    Ben Voigt about 4 years
    @mklement0: Which is pretty darn absurd considering that it passes it to the NetworkCredential constructor which DOES accept a SecureString.
  • K. Frank
    K. Frank about 4 years
    This answer does not work on non windows platforms. PtrToStringAuto is wrong for an explanation see: github.com/PowerShell/PowerShell/issues/…
  • brumScouse
    brumScouse over 2 years
    I'm not sure if you intended it, but the arguments need to have "this" prefixed to be proper extension methods
  • ryanwebjackson
    ryanwebjackson over 2 years
    I believe ToSecureString should only be used for testing as well, because the point of SecureString is to keep the string out of memory.
  • Matt
    Matt over 2 years
    @ryanwebjackson - Yes. And additionally this isn't as good as true encryption. There is no key used so everyone can show the plaintext. So it shouldn't be named "secure". Another example of "security through obscurity".
  • Kit Ramos
    Kit Ramos about 2 years
    to me I think this is the best way you don't have to go about defining new functions or extensions or any of that other overhead. especially nice when you only need to do this once and would rather not have to add a ton of code.