2012-01-15 24 views
19

Quiero brindarle al usuario la capacidad de guardar datos personales encriptados. Esto podría ser trivial o podría haberse preguntado ya, pero no puedo encontrar un ejemplo de una forma fácil de usar para cifrar/descifrar una contraseña.¿Cómo puedo cifrar la configuración del usuario (como las contraseñas) en mi aplicación?

Realmente no necesito ninguna contraseña súper mágica-irrompible. Solo necesito que la contraseña sea difícil de romper.

He visto algunas preguntas sobre msdn y SO, pero no he encontrado algo para usar.

+0

pregunta relacionada: [? Cómo cifrar una contraseña para guardarlo después en un archivo de base de datos o texto] (http://stackoverflow.com/questions/ 541219/how-to-encrypt-a-password-for-saving-it-later-in-a-database-or-text-file). Y mucho, mucho más: [Preguntas que contienen 'C# encrypt password'] (http://stackoverflow.com/search?q=c%23+encrypt+password). –

+4

Tenga en cuenta que hagas lo que hagas, ** no * descifrar * contraseñas **. Más bien, te aseguras de que los dos hashes coincidan. –

+1

@CodyGray ¿hay alguna manera de enviar la contraseña hash en la cadena de conexión sql? – Odys

Respuesta

31

David, pensé your answer era ingenioso, pero pensé que esos serían niftier como los métodos de extensión . Eso permitiría tales como la sintaxis:

string cypherText; 
string clearText; 

using (var secureString = "Some string to encrypt".ToSecureString()) 
{ 
    cypherText = secureString.EncryptString(); 
} 

using (var secureString = cypherText.DecryptString()) 
{ 
    clearText = secureString.ToInsecureString(); 
} 

Aquí está el código de actualización:

using System; 
using System.Collections.Generic; 
using System.Runtime.InteropServices; 
using System.Security; 
using System.Security.Cryptography; 
using System.Text; 

public static class SecureIt 
{ 
    private static readonly byte[] entropy = Encoding.Unicode.GetBytes("Salt Is Not A Password"); 

    public static string EncryptString(this SecureString input) 
    { 
     if (input == null) 
     { 
      return null; 
     } 

     var encryptedData = ProtectedData.Protect(
      Encoding.Unicode.GetBytes(input.ToInsecureString()), 
      entropy, 
      DataProtectionScope.CurrentUser); 

     return Convert.ToBase64String(encryptedData); 
    } 

    public static SecureString DecryptString(this string encryptedData) 
    { 
     if (encryptedData == null) 
     { 
      return null; 
     } 

     try 
     { 
      var decryptedData = ProtectedData.Unprotect(
       Convert.FromBase64String(encryptedData), 
       entropy, 
       DataProtectionScope.CurrentUser); 

      return Encoding.Unicode.GetString(decryptedData).ToSecureString(); 
     } 
     catch 
     { 
      return new SecureString(); 
     } 
    } 

    public static SecureString ToSecureString(this IEnumerable<char> input) 
    { 
     if (input == null) 
     { 
      return null; 
     } 

     var secure = new SecureString(); 

     foreach (var c in input) 
     { 
      secure.AppendChar(c); 
     } 

     secure.MakeReadOnly(); 
     return secure; 
    } 

    public static string ToInsecureString(this SecureString input) 
    { 
     if (input == null) 
     { 
      return null; 
     } 

     var ptr = Marshal.SecureStringToBSTR(input); 

     try 
     { 
      return Marshal.PtrToStringBSTR(ptr); 
     } 
     finally 
     { 
      Marshal.ZeroFreeBSTR(ptr); 
     } 
    } 
} 
+0

Buena mejora. Copié el código de un antiguo proyecto de prueba de unidad en el que estaba usando cuentas de usuario reales y contraseñas para probar. Quería guardar las contraseñas de forma segura para poder ejecutar las pruebas sin tener que mostrar o registrar contraseñas en texto sin formato. –

+0

Consulte la nota que dejo abajo en la respuesta de @DavidClarke, y vea este enlace de msdn relevante (https://msdn.microsoft.com/en-us/library/system.security.securestring (v = vs.110)) .aspx) – Matt

16

Lo siguiente es lo más simple posible, suponiendo que realmente solo desea encriptar/desencriptar una cadena y almacenarla en el disco. Tenga en cuenta que esto no usa una contraseña, utiliza el contexto de seguridad del usuario conectado System.Security.Cryptography.DataProtectionScope.CurrentUser para proteger los datos.

public class SecureIt 
{ 
    static byte[] entropy = System.Text.Encoding.Unicode.GetBytes("Salt Is Not A Password"); 

    public static string EncryptString(System.Security.SecureString input) 
    { 
     byte[] encryptedData = System.Security.Cryptography.ProtectedData.Protect(
      System.Text.Encoding.Unicode.GetBytes(ToInsecureString(input)), 
      entropy, 
      System.Security.Cryptography.DataProtectionScope.CurrentUser); 
     return Convert.ToBase64String(encryptedData); 
    } 

    public static SecureString DecryptString(string encryptedData) 
    { 
     try 
     { 
      byte[] decryptedData = System.Security.Cryptography.ProtectedData.Unprotect(
       Convert.FromBase64String(encryptedData), 
       entropy, 
       System.Security.Cryptography.DataProtectionScope.CurrentUser); 
      return ToSecureString(System.Text.Encoding.Unicode.GetString(decryptedData)); 
     } 
     catch 
     { 
      return new SecureString(); 
     } 
    } 

    public static SecureString ToSecureString(string input) 
    { 
     SecureString secure = new SecureString(); 
     foreach (char c in input) 
     { 
      secure.AppendChar(c); 
     } 
     secure.MakeReadOnly(); 
     return secure; 
    } 

    public static string ToInsecureString(SecureString input) 
    { 
     string returnValue = string.Empty; 
     IntPtr ptr = System.Runtime.InteropServices.Marshal.SecureStringToBSTR(input); 
     try 
     { 
      returnValue = System.Runtime.InteropServices.Marshal.PtrToStringBSTR(ptr); 
     } 
     finally 
     { 
      System.Runtime.InteropServices.Marshal.ZeroFreeBSTR(ptr); 
     } 
     return returnValue; 
    } 

} 

Entonces para cifrar una cadena:

var clearText = "Some string to encrypt"; 
    var cypherText = SecureIt.EncryptString(SecureIt.ToSecureString(clearText)); 

Y para descifrar posteriormente:

var clearText = SecureIt.ToInsecureString(SecureIt.DecryptString(cypherText)); 
+2

Si descifra una cadena segura, no tiene sentido usar una cadena segura. Todo el propósito es mantener la cadena completa fuera de un solo bloque de memoria. No es mejor usar este método de lo que sería si solo tomara una cadena y la encriptara: 'Un objeto SecureString nunca debería construirse a partir de una Cadena, porque los datos confidenciales ya están sujetos a las consecuencias de persistencia de la memoria del clase de cuerda inmutable. La mejor forma de construir un objeto SecureString es desde una fuente no administrada de carácter a la vez, como la consola.Método ReadKey. – Matt

+0

Ese es un buen punto. No estoy sugiriendo que todos los métodos se utilicen en la misma aplicación. Estaba usando una aplicación de consola separada para capturar la cadena y encriptarla antes de almacenarla de forma segura en la configuración de un proyecto de prueba de unidad. La intención era proporcionar suficiente seguridad para el contexto en el que se estaba utilizando. –

1

Para mi propósito modifico Jesse C. Slicer solución a no utilizar SecureString ya que no es importante para mí para proteger la memoria. Solo necesito serializar cadenas encriptadas. Para hacer este trabajo, el proyecto debe tener referencia en System.Security (no solo con la instrucción).

public static class StringSecurityHelper 
{ 
    private static readonly byte[] entropy = Encoding.Unicode.GetBytes("5ID'&mc %[email protected]%n!G^ fiVn8 *tNh3eB %rDaVijn!.c b"); 

    public static string EncryptString(this string input) 
    { 
     if (input == null) 
     { 
      return null; 
     } 

     byte[] encryptedData = ProtectedData.Protect(Encoding.Unicode.GetBytes(input), entropy, DataProtectionScope.CurrentUser); 

     return Convert.ToBase64String(encryptedData); 
    } 

    public static string DecryptString(this string encryptedData) 
    { 
     if (encryptedData == null) 
     { 
      return null; 
     } 

     try 
     { 
      byte[] decryptedData = ProtectedData.Unprotect(Convert.FromBase64String(encryptedData), entropy, DataProtectionScope.CurrentUser); 

      return Encoding.Unicode.GetString(decryptedData); 
     } 
     catch 
     { 
      return null; 
     } 
    } 
} 

Y utilizarlo:

string cypherText = "My string".EncryptString(); 
string clearText = cypherText.DecryptString(); 
Cuestiones relacionadas