encrypt - system security cryptography c# example
Cómo convertir SecureString a System.String? (7)
Todas las reservas sobre la anulación de SecureString mediante la creación de un System.String de lado , ¿cómo se puede hacer?
¿Cómo puedo convertir un System.Security.SecureString ordinario a System.String?
Estoy seguro de que muchos de ustedes que están familiarizados con SecureString van a responder que uno nunca debe transformar un SecureString en una cadena ordinaria de .NET porque elimina todas las protecciones de seguridad. Lo sé Pero en este momento mi programa hace todo con cadenas normales de todos modos, y estoy tratando de mejorar su seguridad y aunque voy a utilizar una API que me devuelve SecureString, no estoy tratando de usar eso para aumentar mi seguridad.
Soy consciente de Marshal.SecureStringToBSTR, pero no sé cómo tomar ese BSTR y hacer un System.String fuera de él.
Para aquellos que pueden exigir saber por qué querría hacer esto, bueno, estoy tomando una contraseña de un usuario y presentándola como un formulario HTML POST para registrar al usuario en un sitio web. Entonces ... esto realmente tiene que hacerse con almacenamientos intermedios administrados, sin encriptar. Si pudiera tener acceso al búfer no encriptado y sin encriptar, me imagino que podría escribir byte por byte en la secuencia de la red y espero que eso mantenga la contraseña segura todo el camino. Estoy esperando una respuesta para al menos uno de estos escenarios.
Creo que sería mejor para las funciones dependientes de SecureString
encapsular su lógica dependiente en una función anónima para un mejor control sobre la cadena desencriptada en la memoria (una vez fijada).
La implementación para descifrar SecureStrings en este fragmento:
- Pin la cadena en la memoria (que es lo que quiere hacer, pero parece que falta en la mayoría de las respuestas aquí).
- Pase su referencia al delegado Func / Action.
- Frote desde la memoria y suelte el GC en el bloque
finally
.
Obviamente, esto hace que sea mucho más fácil "estandarizar" y mantener a las personas que llaman versus confiar en alternativas menos deseables:
- Devolver la cadena desencriptada de una
string DecryptSecureString(...)
ayuda destring DecryptSecureString(...)
. - Duplicando este código donde sea necesario.
Observe aquí, usted tiene dos opciones:
-
static T DecryptSecureString<T>
que le permite acceder al resultado del delegadoFunc
de la persona que llama (como se muestra en el método de pruebaDecryptSecureStringWithFunc
). -
static void DecryptSecureString
es simplemente una versión "nula" que emplea un delegado deAction
en los casos en que realmente no desea / no necesita devolver nada (como se demostró en el método de pruebaDecryptSecureStringWithAction
).
El uso de ejemplos para ambos se puede encontrar en la clase StringsTest
incluida.
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);
}
}
}
Obviamente, esto no evita el abuso de esta función de la siguiente manera, así que tenga cuidado de no hacer esto:
[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
}
Feliz codificación!
Dang. justo después de publicar esto encontré la respuesta en este artículo . Pero si alguien sabe cómo acceder al buffer no encriptado, no encriptado de IntPtr que expone este método, un byte a la vez para que no tenga que crear un objeto de cadena administrado para mantener alta mi seguridad, por favor agregue una respuesta. :)
static String SecureStringToString(SecureString value)
{
IntPtr bstr = Marshal.SecureStringToBSTR(value);
try
{
return Marshal.PtrToStringBSTR(bstr);
}
finally
{
Marshal.FreeBSTR(bstr);
}
}
En mi opinión, los métodos de extensión son la forma más cómoda de resolver esto.
Tomé a excelente respuesta de y lo puse en una clase de extensión de la siguiente manera, junto con un segundo método que agregué para apoyar la otra dirección (cadena -> cadena segura) también, para que pueda crear una cadena segura y convertirla en una cadena normal después:
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;
}
}
Con esto, ahora puedes simplemente convertir tus cadenas de un lado a otro de la siguiente manera:
// 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
Pero tenga en cuenta que el método de decodificación solo debe usarse para las pruebas.
Obviamente, usted sabe cómo esto derrota el propósito de un SecureString, pero voy a replantearlo de todos modos.
Si desea un trazador de líneas único, intente esto: (.NET 4 y superior solamente)
string password = new System.Net.NetworkCredential(string.Empty, securePassword).Password;
Donde securePassword es un SecureString.
Si usa un StringBuilder
lugar de una string
, puede sobrescribir el valor real en la memoria cuando haya terminado. De esta forma, la contraseña no se mantendrá en la memoria hasta que la recolección de basura la recoja.
StringBuilder.Append(plainTextPassword);
StringBuilder.Clear();
// overwrite with reasonably random characters
StringBuilder.Append(New Guid().ToString());
Use la clase 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);
}
}
Si desea evitar la creación de un objeto de cadena administrado, puede acceder a los datos brutos usando 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);
}
}
// 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;
}