56

Can this be simplified to a one liner? Feel free to completely rewrite it as long as secureString gets initialized properly.

SecureString secureString = new SecureString ();
foreach (char c in "fizzbuzz".ToCharArray())
{
    secureString.AppendChar (c);
}
H H
  • 263,252
  • 30
  • 330
  • 514
Todd Smith
  • 17,084
  • 11
  • 59
  • 78

8 Answers8

70

Just use NetworkCredential. It has the conversion logic built-in.

SecureString ss = new NetworkCredential("", "fizzbuzz").SecurePassword;

As others have noted, all of these techniques strip the security benefits of SecureString, but in certain situations (such as unit tests) this may be acceptable.

Update:

As noted in the comments, NetworkCredential can also be used to convert a SecureString back to a string.

string s = new NetworkCredential("", ss).Password;
Kevin
  • 8,312
  • 4
  • 27
  • 31
  • 2
    and it is not available prior .Net 4.0 – rudolf_franek Oct 01 '13 at 17:01
  • 1
    @JohannesOvermann, what do you mean with *both directions*? (As far as I can see, `NetworkCredential` consumes either a plain text password or a `SecureString` password, but only exposes a plain text password in the former case.) – CodeFox Apr 22 '16 at 14:42
  • @CodeFox: I meant that it can be used to translate String -> SecureString and SecureString -> String. I think it always consumes both and always exposes both. But from your question I take it that the SecureString -> String does not work? – Johannes Overmann Apr 22 '16 at 22:02
  • 1
    @JohannesOvermann, yes - that was my assumption after reading the MSDN documentation of the [NetworkCredential](https://msdn.microsoft.com/en-us/library/system.net.networkcredential(v=vs.100).aspx) class. I have tried it out and I can now confirm your original comment. Thanks again! – CodeFox Apr 25 '16 at 04:39
  • @CodeFox: Thanks for trying it out and for confirming that it actually works! – Johannes Overmann Apr 26 '16 at 15:01
53

You could use Linq:

"fizzbuzz".ToCharArray().ToList().ForEach(p => secureString.AppendChar(p));
Liam
  • 27,717
  • 28
  • 128
  • 190
Sascha
  • 10,231
  • 4
  • 41
  • 65
  • I guess I can throw this into an extension method to get what I'm after: processInfo.Password = new SecureSring ().FromString ("fizzbuzz") – Todd Smith Mar 10 '10 at 21:30
  • 31
    You can avoid the extra .ToList() operation with the following: `Array.ForEach("fizzbuzz".ToCharArray(), secureString.AppendChar);` – Steve Guidi Jan 28 '12 at 00:52
  • You don't have to declare `ToCharArray`, Linq will automatically enumerate as a `char` array. – Greg Aug 15 '16 at 14:52
  • this is still a 2 liner though? You still need to have declared `secureString`. – nagates Mar 30 '17 at 18:34
  • 1
    At least in later versions of .NET (say, 4.5), this is possible `"fizzbuzz".ForEach(secureString.AppendChar)` – jpaugh May 30 '17 at 20:53
31

Apart from using unsafe code and a char*, there isn't a (much) better way.

The point here is not to copy SecureString contents to/from normal strings. The constant "fizzbuzz" constant is the security leak here.

H H
  • 263,252
  • 30
  • 330
  • 514
  • Beat me to it -- +1. Plus the additional changes you need to make to allow for unsafe code negates any "savings" on lines of code. – Austin Salonen Mar 10 '10 at 20:08
  • 1
    Don't most passwords originate in most software as strings and then need to be converted to a SecureString? Not sure what you mean by "not to copy SecureString contents from normal string". In normal circumstances that would be string password. "fizzbuzz" is just a homage. – Todd Smith Mar 10 '10 at 21:35
  • 1
    Yes, and that greatly reduces the usability of SecureString. – H H Mar 10 '10 at 22:04
  • SecureString is a property of ProcessStartInfo and is needed for Process.Start(). Blame MS not the messenger :) – Todd Smith Mar 10 '10 at 22:21
  • 14
    If you're collecting a SecureString from keystrokes, you don't actually have an original string. This, I believe, was the original intent of SecureString. – Doug Aug 25 '10 at 16:11
  • 1
    I've yet to find a Q&A that talks about how to capture string that you want to make secure into the process to begin with. For example, say I fetch a password from user input from a password form control on a windows form. How could I get that input securely? Wouldn't it already be in a `string` the moment I get it from the control, negating my attempts at securing it? Can I force garbage collection on the `string` after retrieving it from the control and placing it into my `SecureString`? – crush Feb 20 '18 at 20:26
  • Yes, (G)UI support is all but absent. The WPF passwordbox does have a securestring property but I don't know how good (secure) that is implemented. – H H Feb 20 '18 at 21:48
18
var s = "fizzbuzz".Aggregate(new SecureString(), (ss, c) => { ss.AppendChar(c); return ss; });
Kevin
  • 8,312
  • 4
  • 27
  • 31
18

Slight improvement on Sascha's answer replacing the lambda with a method group

"fizzbuzz".ToCharArray().ToList().ForEach(ss.AppendChar);
Peter Kelly
  • 14,253
  • 6
  • 54
  • 63
9

Here is a how NetworkCredential class from .NET doing it:

SecureString secureString;
fixed (char* chPtr = plainString)
  secureString = new SecureString(chPtr, plainString.Length);

Ugly but probably the most efficient.

Boris Lipschitz
  • 9,236
  • 5
  • 53
  • 63
  • 10
    Note that the use of `fixed` requires an `unsafe` block, which in turn requires the compiler switch `/unsafe`. – DonBoitnott Apr 25 '14 at 11:36
9

Since SecureString utilizes the IDispose interface. You could actually do it like this.

SecureString secure = new SecureString();
foreach(var character in data.ToCharArray())
    secure.AppendChar(character);

Essentially the data would be a parameter.

If you utilize the using to help alleviate resources; you'll want to be careful about the scope. But this may be a beneficial alternative, depending on usage.

Update:

You could actually do a full method signature:

public static SecureString ConvertStringToSecureString(this string data)
{
     var secure = new SecureString();
     foreach(var character in data.ToCharArray())
         secure.AppendChar(character);

     secure.MakeReadOnly();
     return secure;
     
}

For the decryption you would want to do:

public static string ConvertSecureStringToString(this SecureString data)
{
     var pointer = IntPtr.Zero;
     try
     {
          pointer = Marshal.SecureStringToGlobalAllocUnicode(data);
          return Marshal.PtrToStringUni(pointer);
     }
     finally
     {
          Marshal.ZeroFreeGlobalAllocUnicode(pointer);
     }
}

The following article will give you some additional information as well.

Rey
  • 3,663
  • 3
  • 32
  • 55
Greg
  • 11,302
  • 2
  • 48
  • 79
  • Why didn't MS add this to the framework? The class is completely useless if you can't access the value. Seems like they (MS) either didn't really want people to use SecureString, or are even more inept than I previously have accused. – Maxx May 15 '19 at 19:22
5

least amount of code because .ToList() is not required for this:

Array.ForEach("fizzbuzz".ToCharArray(), secureString.AppendChar);
Gerrie Pretorius
  • 3,381
  • 2
  • 31
  • 34