184

How to convert String to SecureString?

Hossein Narimani Rad
  • 31,361
  • 18
  • 86
  • 116
Developer404
  • 5,716
  • 16
  • 64
  • 102

15 Answers15

251

There is also another way to convert between SecureString and String.

1. String to SecureString

SecureString theSecureString = new NetworkCredential("", "myPass").SecurePassword;

2. SecureString to String

string theString = new NetworkCredential("", theSecureString).Password;

Here is the link

Hossein Narimani Rad
  • 31,361
  • 18
  • 86
  • 116
  • 5
    This is the best solution by far for the most common use-case! – Dan Bechard Apr 25 '17 at 15:31
  • 25
    There is no point in using a `SecureString` if your code create a `string` object with the value you want to secure. The goal of `SecureString` is avoiding to have the string in the managed memory, so an attacker examining that memory can spot the value you want to hide. Since the `NetworkCredential` constructor you are calling requires a string, that's not the way to go... Sure, your code is an easy way to convert to and from a SecureString, but using it makes your code as safe as using a simple `string` – Gian Paolo Feb 04 '18 at 17:40
  • 30
    @GianPaolo while that is correct in theory, in practice we still need to use `SecureString` when working with some libraries. And lets be honest here, if someone is actively scanning the memory of your application then you've already lost. Even if you have used SecureString correctly, which I've never seen, there is going to be other valuable data to extract. – Jonathan Allen Sep 17 '18 at 22:02
  • 1
    @JonathanAllen: You're probably right, but only because we have a culture of treating security as an afterthought. If you're creating a recipe website, North Korea isn't likely to try scanning your program's memory. If you're writing banking software, the threat is much more real. – Eric J. Oct 26 '19 at 22:37
  • 1
    Very useful conversion. Of course I am building secure software using SecureString for the passwords, but for compatibility with older or simpler systems we need to also support passwords in a regular string, or I will get fired :-) – Roland Apr 13 '21 at 09:59
  • heh this answer has move upvotes than the link it was taken from. Nicely done :) – Fuzzy Analysis Apr 28 '22 at 00:06
  • 1
    @GianPaolo, copying a string into a SecureString is useful for automated testing. – Keith Russell Jan 03 '23 at 15:48
  • @GianPaolo and local development – TamusJRoyce Apr 27 '23 at 19:18
164

You don't. The whole reason for using the SecureString object is to avoid creating a string object (which is loaded into memory and kept there in plaintext until garbage collection). However, you can add characters to a SecureString by appending them.

var s = new SecureString();
s.AppendChar('d');
s.AppendChar('u');
s.AppendChar('m');
s.AppendChar('b');
s.AppendChar('p');
s.AppendChar('a');
s.AppendChar('s');
s.AppendChar('s');
s.AppendChar('w');
s.AppendChar('d');
John Dagg
  • 1,672
  • 1
  • 10
  • 2
  • 29
    "The combination is `12345`... that's the kind of thing an idiot has on his luggage!" – Kellen Stuart Sep 27 '17 at 23:47
  • 18
    All I see is `*****` – Ian Boyd Nov 21 '17 at 19:48
  • `AppendChar` doesn't execute until run time, so those characters can still be read from the IL, correct? Maybe some compile-time obfuscation would help as well... that is, if you're hard-coding passwords. – samus Aug 22 '18 at 18:56
  • 9
    That's a lame argument. Where do you get a password? From a form or from a command line. Until they create a method to read a secure string directly from a device or command line arguments, we'll need to convert a string into a SecureString. – Quark Soup Apr 11 '19 at 23:08
  • 3
    @DonaldAirey you get a password from a secrets manager. Generally the secrets we're talking about here are not user passwords, but things that potentially lead to privilege escalation or data leaks. That means the passwords are for other services, and shouldn't be passed on command lines or in the clear over network channels. – Barry Kelly Jun 21 '19 at 14:24
80

below method helps to convert string to secure string

private SecureString ConvertToSecureString(string password)
{
    if (password == null)
        throw new ArgumentNullException("password");

    var securePassword = new SecureString();

    foreach (char c in password)
        securePassword.AppendChar(c);

    securePassword.MakeReadOnly();
    return securePassword;
}
Yusuf Tarık Günaydın
  • 3,016
  • 2
  • 27
  • 41
M2012
  • 3,465
  • 2
  • 20
  • 27
  • 37
    +1, but note that passing the password in as a string parameter creates an unencrypted string instance in managed memory and defeats the purpose of 'SecureString' in the first place. Just pointing this out for future people who come by and use this code. – Cody Jan 25 '17 at 21:47
  • 29
    @Cody That's true, and a good point, but many of us don't care whether the SecureString is secure or not and are only using it because some Microsoft API requires it as a parameter. – Dan Bechard Apr 25 '17 at 15:17
  • 9
    @Cody Turns out that even the SecureString class can't keep the string encrypted. Which is why MS are considering obsoleting the type https://github.com/dotnet/platform-compat/blob/master/docs/DE0001.md. – Martin Brown Aug 22 '19 at 15:30
25

You can follow this:

string password = "test";
SecureString sec_pass = new SecureString();
Array.ForEach(password.ToArray(), sec_pass.AppendChar);
sec_pass.MakeReadOnly();
Scorpio
  • 381
  • 3
  • 2
17

Here is a cheap linq trick.

            SecureString sec = new SecureString();
            string pwd = "abc123"; /* Not Secure! */
            pwd.ToCharArray().ToList().ForEach(sec.AppendChar);
            /* and now : seal the deal */
            sec.MakeReadOnly();
Gabriel Marius Popescu
  • 2,016
  • 2
  • 20
  • 22
granadaCoder
  • 26,328
  • 10
  • 113
  • 146
  • 50
    This answer is an exercise is seeing how many temporary copies of the plain text can be generated in one shot – Mike Caron May 16 '16 at 20:43
  • 1
    as @john-dagg suggested the idea is to NOT set a string with your password because if you do there is no advantage left to use a securestring. In your case you have put in the password in plain text, you are securing nothing by later on using a securestring. I hope you understand that the securestring was meant to secure your string from people looking at the disassembly or a debugger so see whats in the IL/memory. – user734028 Jul 20 '19 at 07:39
  • 2
    Correct in an ideal world. But sometimes you have to .. have-a-string. I can code up all my (layered) code to SecureString, but when I (ultimately) make a rest call that requires a client-secret (as a plain string)......then that's what I gotta do. How I handle this is.. wait til the last possible moment to make it a string, use it, and (not really) destroy the string itself, and (try as best as I can) destroy the SecureString. it is a "best attempt" to try and minimize exposure. yes i understand the IL/memory "usage". IMHO:Microsoft should have named this object "KindaSecureString". – granadaCoder Jul 25 '21 at 10:00
  • My previous comment is discussed here: https://docs.microsoft.com/en-us/dotnet/api/system.security.securestring?view=net-5.0#HowSecure. "Storage versus usage More generally, the SecureString class defines a storage mechanism for string values that should be protected or kept confidential. However, outside of the .NET Framework itself, no usage mechanism supports SecureString. This means that the secure string must be converted to a usable form (typically a clear text form) that can be recognized by its target, and that decryption and conversion must occur in user space." #cantVetoEverything – granadaCoder Jul 26 '21 at 11:05
12

I just want to point out to all the people saying, "That's not the point of SecureString", that many of the people asking this question might be in an application where, for whatever reason, justified or not, they are not particularly concerned about having a temporary copy of the password sit on the heap as a GC-able string, but they have to use an API that only accepts SecureString objects. So, you have an app where you don't care whether the password is on the heap, maybe it's internal-use only and the password is only there because it's required by the underlying network protocols, and you find that that string where the password is stored cannot be used to e.g. set up a remote PowerShell Runspace -- but there is no easy, straight-forward one-liner to create that SecureString that you need. It's a minor inconvenience -- but probably worth it to ensure that the applications that really do need SecureString don't tempt the authors to use System.String or System.Char[] intermediaries. :-)

Jonathan Gilbert
  • 3,526
  • 20
  • 28
10

I'll throw this out there. Why?

You can't just change all your strings to secure strings and suddenly your application is "secure". Secure string is designed to keep the string encrypted for as long as possible, and only decrypted for a very short period of time, wiping the memory after an operation has been performed upon it.

I would hazard saying that you may have some design level issues to deal with before worrying about securing your application strings. Give us some more information on what your trying to do and we may be able to help better.

Spence
  • 28,526
  • 15
  • 68
  • 103
  • 1
    I just want to use it to set password for ProcessStartInfo and run my exe as different user.. That worked for me... – Developer404 Oct 15 '09 at 11:12
  • 5
    My application is secured by running it on a secure system. I couldn't care less about this particular string being encrypted in memory. There are far more vital pieces of information on this system to worry about should it ever be compromised. SecureString is simply required by a Microsoft API that my program needs to utilize. It is implied that the developer consider his or her own use-case and determine whether converting Strings to SecureStrings is a valid operation in context. – Dan Bechard Apr 25 '17 at 15:20
  • So this method won't prevent someone to see the password if he use a decompiler like DotPeek? Is there a way to hide strings in this case? – M.Parent Jul 05 '19 at 15:38
  • There are a few methods, depending on the threat you are trying to defeat. If it's to protect the users information from anyone but the local administrator, you can use the DPAPI methods on windows to encrypt a file and store a secret there, read it into a secure string and then throw it away. If it's for a company secret, then short answer is if your application can decrypt it, so can the user eventually. – Spence Jul 08 '19 at 12:04
  • Yes, I agree. This article fragment gets into it a little. https://docs.microsoft.com/en-us/dotnet/api/system.security.securestring?view=net-5.0#HowSecure Storage versus usage More generally, the SecureString class defines a storage mechanism for string values that should be protected or kept confidential. However, outside of the .NET Framework itself, no usage mechanism supports SecureString. This means that the secure string must be converted to a usable form (typically a clear text form) that can be recognized by its target, and that decryption and conversion must occur in user space. – granadaCoder Jul 26 '21 at 11:48
  • https://learn.microsoft.com/en-us/dotnet/api/system.security.securestring?view=net-5.0#HowSecure Overall, SecureString is more secure than String because it limits the exposure of sensitive string data. However, those strings may still be exposed to any process or operation that has access to raw memory, such as a malicious process running on the host computer, a process dump, or a user-viewable swap file. Instead of using SecureString to protect passwords, the recommended alternative is to use an opaque handle to credentials that are stored outside of the process. – granadaCoder Jul 26 '21 at 11:48
  • But basically, you cannot "always find another way". My example would be, you have layers in your dotnet code and classes and methods where you pass around a SecureSTring.......BUT if I ultimately have to make a call to a Oauth/STS...that takes a plain string "client_secret"......at that point, I "gotta have a string". How I handle this is.. wait til the last possible moment to make it a string, use it, and (not really) destroy the string itself, and (try as best as I can) destroy the SecureString. it is a "best attempt" to try and minimize exposure. yes i understand the IL/memory "usage". – granadaCoder Jul 26 '21 at 11:50
  • IMHO:Microsoft should have named this object "KindaSecureString". and IMHO : "#cantVetoEverything" as per the MS article "limits the exposure of sensitive string data". Aka, it limits, but is not a magic-bull3t. – granadaCoder Jul 26 '21 at 11:51
  • @M.Parent Encrypt the strings before you store them in the config – AlphaG33k Dec 14 '22 at 22:47
10
unsafe 
{
    fixed(char* psz = password)
        return new SecureString(psz, password.Length);
}
orad
  • 15,272
  • 23
  • 77
  • 113
hypersw
  • 475
  • 3
  • 9
6

no fancy linq, not adding all the chars by hand, just plain and simple:

var str = "foo";
var sc = new SecureString();
foreach(char c in str) sc.appendChar(c);
garglblarg
  • 576
  • 1
  • 8
  • 16
4

I'm agree with Spence (+1), but if you're doing it for learning or testing pourposes, you can use a foreach in the string, appending each char to the securestring using the AppendChar method.

Jonathan
  • 11,809
  • 5
  • 57
  • 91
4

The following 2 extensions should do the trick:

  1. For a char array

    public static SecureString ToSecureString(this char[] _self)
    {
        SecureString knox = new SecureString();
        foreach (char c in _self)
        {
            knox.AppendChar(c);
        }
        return knox;
    }
    
  2. And for string

    public static SecureString ToSecureString(this string _self)
    {
        SecureString knox = new SecureString();
        char[] chars = _self.ToCharArray();
        foreach (char c in chars)
        {
            knox.AppendChar(c);
        }
        return knox;
    }
    

Thanks to John Dagg for the AppendChar recommendation.

Peet
  • 697
  • 9
  • 9
3

If you would like to compress the conversion of a string to a SecureString into a LINQ statement you can express it as follows:

var plain  = "The quick brown fox jumps over the lazy dog";
var secure = plain
             .ToCharArray()
             .Aggregate( new SecureString()
                       , (s, c) => { s.AppendChar(c); return s; }
                       , (s)    => { s.MakeReadOnly(); return s; }
                       );

However, keep in mind that using LINQ does not improve the security of this solution. It suffers from the same flaw as any conversion from string to SecureString. As long as the original string remains in memory the data is vulnerable.

That being said, what the above statement can offer is keeping together the creation of the SecureString, its initialization with data and finally locking it from modification.

cel sharp
  • 149
  • 9
1

For completeness, I added two unit tests and methods to convert from char array and string into SecureString and back again. You should try to avoid strings altogether and only pass in either a pointer to a char array or the char array itself as in the methods I provide here, as strings are unsafe since they keep data in plain text in managed memory for rather indeterministic amount of time until next GC is run or forced, it is better to put your character array as soon as possible into a SecureString and keep it there and read it back again as a character array also.

The tests look like this:

using NUnit.Framework;
using System;
using SecureStringExtensions;
using System.Security;

namespace SecureStringExtensions.Test
{
    [TestFixture]
    public class SecureStringExtensionsTest
    {
        [Test]
        [TestCase(new char[] { 'G', 'O', 'A', 'T', '1', '2', '3' })]
        public void CopyCharArrayToSecureStringAndCopyBackToCharArrayReturnsExpected(char[] inputChars)
        {
            SecureString sec = inputChars.ToSecureString();
            var copiedFromSec = sec.FromSecureStringToCharArray();
            CollectionAssert.AreEqual(copiedFromSec, inputChars);                
        }

        [Test]
        [TestCase("GOAT456")]
        public void CopyStringToSecureStringAndCopyBackToUnsafeStringReturnsExpected(string inputString)
        {
            SecureString sec = inputString.ToSecureString();
            var copiedFromSec = sec.FromSecureStringToUnsafeString();
            Assert.AreEqual(copiedFromSec, inputString);
        }
    }
}

And we have our extension methods here:

using System;
using System.Runtime.InteropServices;
using System.Security;

namespace SecureStringExtensions
{
    public static class SecureStringExtensions
    {
        public static SecureString ToSecureString(this string str)
        {
            return ToSecureString(str.ToCharArray());
        }

        public static SecureString ToSecureString(this char[] str)
        {
            var secureString = new SecureString();
            Array.ForEach(str, secureString.AppendChar);
            return secureString;
        }

        /// <summary>
        /// Creates a managed character array from the secure string using methods in System.Runetime.InteropServices
        /// copying data into a BSTR (unmanaged binary string) and then into a managed character array which is returned from this method.
        /// Data in the unmanaged memory temporarily used are freed up before the method returns.
        /// </summary>
        /// <param name="secureString"></param>
        /// <returns></returns>
        public static char[] FromSecureStringToCharArray(this SecureString secureString)
        {
            char[] bytes;
            var ptr = IntPtr.Zero;
            try
            {
                //alloc unmanaged binary string  (BSTR) and copy contents of SecureString into this BSTR
                ptr = Marshal.SecureStringToBSTR(secureString);
                bytes = new char[secureString.Length];
                //copy to managed memory char array from unmanaged memory 
                Marshal.Copy(ptr, bytes, 0, secureString.Length);
            }
            finally
            {
                if (ptr != IntPtr.Zero)
                {
                    //free unmanaged memory
                    Marshal.ZeroFreeBSTR(ptr);
                }
            }
            return bytes;
        }

        /// <summary>
        /// Returns an unsafe string in managed memory from SecureString. 
        /// The use of this method is not recommended - use instead the <see cref="FromSecureStringToCharArray(SecureString)"/> method
        /// as that method has not got multiple copies of data in managed memory like this method.
        /// Data in unmanaged memory temporarily used are freed up before the method returns.
        /// </summary>
        /// <param name="secureString"></param>
        /// <returns></returns>
        public static string FromSecureStringToUnsafeString(this SecureString secureString)
        {
            if (secureString == null)
            {
                throw new ArgumentNullException(nameof(secureString));
            }
            var unmanagedString = IntPtr.Zero;
            try
            {
                //copy secure string into unmanaged memory
                unmanagedString = Marshal.SecureStringToGlobalAllocUnicode(secureString);
                //alloc managed string and copy contents of unmanaged string data into it
                return Marshal.PtrToStringUni(unmanagedString);
            }
            finally
            {
                    if (unmanagedString != IntPtr.Zero)
                {
                    Marshal.FreeBSTR(unmanagedString);
                }
            }
        }

    }
}

Here, we use methods from System.Runtime.InteropServices to alloc BSTR (binary unmanaged string) temporarily and also make sure to free up temporary used unmanaged memory.

Tore Aurstad
  • 3,189
  • 1
  • 27
  • 22
0

I find it amazing how I am still learning, even when it comes down to handling sensitive strings!

As per the DotNet docs: https://github.com/dotnet/platform-compat/blob/master/docs/DE0001.md

Don't use SecureString for new code. When porting code to .NET Core, consider that the contents of the array are not encrypted in memory. The general approach of dealing with credentials is to avoid them and instead rely on other means to authenticate, such as certificates or Windows authentication.

Additionally, a common "truly-secure" way to secure sensitive strings would be to encrypt them in the config file itself, since there is no secure string concept at the OS level.

AlphaG33k
  • 1,588
  • 1
  • 12
  • 24
-1

you can use this simple script

private SecureString SecureStringConverter(string pass)
{
    SecureString ret = new SecureString();

    foreach (char chr in pass.ToCharArray())
        ret.AppendChar(chr);

    return ret;
}
Farhad
  • 4,119
  • 8
  • 43
  • 66
nadir
  • 63
  • 1
  • 11
  • 5
    This answer [was posted 15 months ago](https://stackoverflow.com/a/38326325/11683). There was really no need to post it again. – GSerg Oct 28 '17 at 06:57