#region Copyright 2010-2014 by Roger Knapp, Licensed under the Apache License, Version 2.0 /* Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #endregion using System; using System.Collections.Generic; using System.Security; using System.Text; using System.Runtime.InteropServices; using System.IO; namespace CSharpTest.Net.Crypto { /// /// Various utility methods for access to secure strings. Lets be real about this before you /// go off, SecureString is NOT secure, it obfuscated. If your in the process you can access /// it's contents; however, if you looking at a crash dump or swap file then the SecureString /// provides value... just not much ;) So these methods are actually my attempt to get people /// to USE a SecureString or similar class (i.e. Password) rather than continuing to use plain /// text strings. Hopefully with the ease of access within the process we can provide better /// security without. /// public static class SecureStringUtils { /// /// Creates a SecureString from an enumerable set of characters, like: Create("password string"); /// public static SecureString Create(IEnumerable chEnum) { return AppendAll(new SecureString(), chEnum); } /// /// Creates a SecureString from a stream of unicode characters /// public static SecureString Create(Stream io) { return Create(io, Encoding.Unicode); } /// /// Creates a SecureString from an stream of characters /// public static SecureString Create(Stream io, Encoding encoding) { SecureString ss = new SecureString(); using (io) using (TextReader r = new StreamReader(io, encoding, false)) { int ch; while (-1 != (ch = r.Read())) ss.AppendChar((Char)ch); } ss.MakeReadOnly(); return ss; } /// /// Adds the set of characters and makes the string readonly usage: /// SecureString s = new SecureString().AppendAll("This is a password"); /// #if NET20 public static SecureString AppendAll(SecureString sstr, T chEnum) where T : IEnumerable #else public static SecureString AppendAll(this SecureString sstr, T chEnum) where T : IEnumerable #endif { foreach (char ch in chEnum) sstr.AppendChar(ch); sstr.MakeReadOnly(); return sstr; } /// /// Returns a stream of Unicode bytes from the give SecureString instance /// #if NET20 public static TextReader ToTextReader(SecureString data) #else public static TextReader ToTextReader(this SecureString data) #endif { return new UnicodeReader(new SecureStringStream(data), Encoding.Unicode); } /// /// Returns a stream of Unicode bytes from the give SecureString instance /// #if NET20 public static Stream ToStream(SecureString data) #else public static Stream ToStream(this SecureString data) #endif { return new SecureStringStream(data); } /// /// Converts a System.Security.SecureString into an array of bytes using System.Text.Encoding.Unicode /// #if NET20 public static byte[] ToByteArray(SecureString data) #else public static byte[] ToByteArray(this SecureString data) #endif { return ToByteArray(data, Encoding.Unicode); } /// /// Converts a System.Security.SecureString into an array of bytes using the Encoding specified /// #if NET20 public static byte[] ToByteArray(SecureString data, Encoding encoding) #else public static byte[] ToByteArray(this SecureString data, Encoding encoding) #endif { Char[] chars = new Char[data.Length]; GCHandle hchars = GCHandle.Alloc(chars, GCHandleType.Pinned); try { CopyChars(data, 0, (char[])hchars.Target, 0, data.Length); return encoding.GetBytes(chars); } finally { Array.Clear(chars, 0, chars.Length); hchars.Free(); } } /// /// Returns the secure string as an array of characters /// #if NET20 public static Char[] ToCharArray(SecureString data) #else public static Char[] ToCharArray(this SecureString data) #endif { Char[] chars = new Char[data.Length]; CopyChars(data, 0, chars, 0, data.Length); return chars; } /// /// Copies the specified range of characters from the secure string to the output character array. /// #if NET20 public static void CopyChars(SecureString input, int inputOffset, Char[] output, int outputOffset, int outputLength) #else public static void CopyChars(this SecureString input, int inputOffset, Char[] output, int outputOffset, int outputLength) #endif { Check.NotNull(input); Check.ArraySize(output, outputOffset + outputLength, int.MaxValue); Check.InRange(inputOffset, 0, input.Length); Check.InRange(outputOffset, 0, output.Length); Check.InRange(outputLength, 0, Math.Min(input.Length - inputOffset, output.Length - outputOffset)); IntPtr pb = Marshal.SecureStringToBSTR(input); try { IntPtr from = inputOffset == 0 ? pb : new IntPtr(pb.ToInt64() + (inputOffset << 1)); Marshal.Copy(from, output, outputOffset, outputLength); } finally { Marshal.ZeroFreeBSTR(pb); } } } }