it-swarm.com.de

Verschlüsseln und Entschlüsseln einer Zeichenfolge in C #

Was ist die modernste (beste) Methode, um die folgenden Anforderungen in C # zu erfüllen?

string encryptedString = SomeStaticClass.Encrypt(sourceString);

string decryptedString = SomeStaticClass.Decrypt(encryptedString);

ABER mit einem Minimum an Aufhebens um Salz, Schlüssel, herumspielen mit Byte [] usw.

Ich habe gegoogelt und war verwirrt über das, was ich gefunden habe (Sie können die Liste ähnlicher SO Qs sehen, um zu sehen, dass dies eine trügerische Frage ist).

316
Richard

UPDATE 23/Dec/2015: Da diese Antwort anscheinend viele positive Stimmen erhält, habe ich sie aktualisiert, um dumme Fehler zu beheben und den Code basierend auf Kommentaren und Feedback zu verbessern. Eine Liste der spezifischen Verbesserungen finden Sie am Ende des Beitrags.

Wie bereits erwähnt, ist Kryptografie nicht einfach. Vermeiden Sie daher am besten, Ihren eigenen Verschlüsselungsalgorithmus zu verwenden.

Sie können jedoch Ihre eigene Wrapper-Klasse um so etwas wie die integrierte Kryptografie-Klasse RijndaelManaged rollen.

Rijndael ist der algorithmische Name des aktuellen Advanced Encryption Standard , daher verwenden Sie mit Sicherheit einen Algorithmus, der als "Best Practice" angesehen werden kann.

Die Klasse RijndaelManaged verlangt normalerweise, dass Sie mit Byte-Arrays, Salt, Schlüsseln, Initialisierungsvektoren usw. "herumfummeln", aber genau diese Art von Detail kann in Ihrem Wrapper etwas abstrahiert werden "Klasse.

Die folgende Klasse ist eine, die ich vor einiger Zeit geschrieben habe, um genau das zu tun, wonach Sie suchen: Ein einfacher Methodenaufruf, mit dem ein Zeichenfolgen-basierter Klartext mit einem Zeichenfolgen-basierten Kennwort verschlüsselt werden kann, und die resultierende verschlüsselte Zeichenfolge auch als Zeichenfolge dargestellt werden. Natürlich gibt es eine äquivalente Methode, um die verschlüsselte Zeichenfolge mit demselben Kennwort zu entschlüsseln.

Im Gegensatz zur ersten Version dieses Codes, bei der jedes Mal exakt die gleichen Salz- und IV-Werte verwendet wurden, generiert diese neuere Version jedes Mal zufällige Salz- und IV-Werte. Da Salt und IV zwischen der Verschlüsselung und Entschlüsselung eines bestimmten Strings gleich sein müssen, werden Salt und IV beim Verschlüsseln dem Chiffretext vorangestellt und erneut daraus extrahiert, um die Entschlüsselung durchzuführen. Dies hat zur Folge, dass das Verschlüsseln des exakt gleichen Klartextes mit dem exakt gleichen Passwort jedes Mal ein völlig anderes Chiffretext-Ergebnis ergibt.

Die "Stärke" bei der Verwendung dieser Funktion liegt in der Verwendung der Klasse RijndaelManaged , um die Verschlüsselung für Sie durchzuführen, sowie in der Verwendung der Funktion Rfc2898DeriveBytes des Namespace _System.Security.Cryptography_, der dies bewirkt Generieren Sie Ihren Verschlüsselungsschlüssel mithilfe eines standardmäßigen und sicheren Algorithmus (insbesondere PBKDF2 ), der auf dem von Ihnen angegebenen stringbasierten Kennwort basiert. (Beachten Sie, dass dies eine Verbesserung der Verwendung des älteren PBKDF1-Algorithmus in der ersten Version ist.).

Abschließend ist zu beachten, dass dies immer noch eine nicht authentifizierte Verschlüsselung ist. Verschlüsselung allein bietet nur Datenschutz (d. H. Die Nachricht ist Dritten unbekannt), während authentifizierte Verschlüsselung sowohl Datenschutz als auch Authentizität bietet (d. H. Der Empfänger weiß, dass die Nachricht vom Absender gesendet wurde).

Ohne Ihre genauen Anforderungen zu kennen, ist es schwierig zu sagen, ob der Code hier für Ihre Anforderungen ausreichend sicher ist. Er wurde jedoch erstellt, um ein ausgewogenes Verhältnis zwischen relativer Einfachheit der Implementierung und "Qualität" zu erzielen. Wenn beispielsweise Ihr "Empfänger" einer verschlüsselten Zeichenfolge die Zeichenfolge direkt von einem vertrauenswürdigen "Absender" empfängt, ist die Authentifizierung möglicherweise nicht einmal erforderlich .

Wenn Sie etwas Komplexeres benötigen, das authentifizierte Verschlüsselung bietet, lesen Sie diesen Beitrag für eine Implementierung.

Hier ist der Code:

_using System;
using System.Text;
using System.Security.Cryptography;
using System.IO;
using System.Linq;

namespace EncryptStringSample
{
    public static class StringCipher
    {
        // This constant is used to determine the keysize of the encryption algorithm in bits.
        // We divide this by 8 within the code below to get the equivalent number of bytes.
        private const int Keysize = 256;

        // This constant determines the number of iterations for the password bytes generation function.
        private const int DerivationIterations = 1000;

        public static string Encrypt(string plainText, string passPhrase)
        {
            // Salt and IV is randomly generated each time, but is preprended to encrypted cipher text
            // so that the same Salt and IV values can be used when decrypting.  
            var saltStringBytes = Generate256BitsOfRandomEntropy();
            var ivStringBytes = Generate256BitsOfRandomEntropy();
            var plainTextBytes = Encoding.UTF8.GetBytes(plainText);
            using (var password = new Rfc2898DeriveBytes(passPhrase, saltStringBytes, DerivationIterations))
            {
                var keyBytes = password.GetBytes(Keysize / 8);
                using (var symmetricKey = new RijndaelManaged())
                {
                    symmetricKey.BlockSize = 256;
                    symmetricKey.Mode = CipherMode.CBC;
                    symmetricKey.Padding = PaddingMode.PKCS7;
                    using (var encryptor = symmetricKey.CreateEncryptor(keyBytes, ivStringBytes))
                    {
                        using (var memoryStream = new MemoryStream())
                        {
                            using (var cryptoStream = new CryptoStream(memoryStream, encryptor, CryptoStreamMode.Write))
                            {
                                cryptoStream.Write(plainTextBytes, 0, plainTextBytes.Length);
                                cryptoStream.FlushFinalBlock();
                                // Create the final bytes as a concatenation of the random salt bytes, the random iv bytes and the cipher bytes.
                                var cipherTextBytes = saltStringBytes;
                                cipherTextBytes = cipherTextBytes.Concat(ivStringBytes).ToArray();
                                cipherTextBytes = cipherTextBytes.Concat(memoryStream.ToArray()).ToArray();
                                memoryStream.Close();
                                cryptoStream.Close();
                                return Convert.ToBase64String(cipherTextBytes);
                            }
                        }
                    }
                }
            }
        }

        public static string Decrypt(string cipherText, string passPhrase)
        {
            // Get the complete stream of bytes that represent:
            // [32 bytes of Salt] + [32 bytes of IV] + [n bytes of CipherText]
            var cipherTextBytesWithSaltAndIv = Convert.FromBase64String(cipherText);
            // Get the saltbytes by extracting the first 32 bytes from the supplied cipherText bytes.
            var saltStringBytes = cipherTextBytesWithSaltAndIv.Take(Keysize / 8).ToArray();
            // Get the IV bytes by extracting the next 32 bytes from the supplied cipherText bytes.
            var ivStringBytes = cipherTextBytesWithSaltAndIv.Skip(Keysize / 8).Take(Keysize / 8).ToArray();
            // Get the actual cipher text bytes by removing the first 64 bytes from the cipherText string.
            var cipherTextBytes = cipherTextBytesWithSaltAndIv.Skip((Keysize / 8) * 2).Take(cipherTextBytesWithSaltAndIv.Length - ((Keysize / 8) * 2)).ToArray();

            using (var password = new Rfc2898DeriveBytes(passPhrase, saltStringBytes, DerivationIterations))
            {
                var keyBytes = password.GetBytes(Keysize / 8);
                using (var symmetricKey = new RijndaelManaged())
                {
                    symmetricKey.BlockSize = 256;
                    symmetricKey.Mode = CipherMode.CBC;
                    symmetricKey.Padding = PaddingMode.PKCS7;
                    using (var decryptor = symmetricKey.CreateDecryptor(keyBytes, ivStringBytes))
                    {
                        using (var memoryStream = new MemoryStream(cipherTextBytes))
                        {
                            using (var cryptoStream = new CryptoStream(memoryStream, decryptor, CryptoStreamMode.Read))
                            {
                                var plainTextBytes = new byte[cipherTextBytes.Length];
                                var decryptedByteCount = cryptoStream.Read(plainTextBytes, 0, plainTextBytes.Length);
                                memoryStream.Close();
                                cryptoStream.Close();
                                return Encoding.UTF8.GetString(plainTextBytes, 0, decryptedByteCount);
                            }
                        }
                    }
                }
            }
        }

        private static byte[] Generate256BitsOfRandomEntropy()
        {
            var randomBytes = new byte[32]; // 32 Bytes will give us 256 bits.
            using (var rngCsp = new RNGCryptoServiceProvider())
            {
                // Fill the array with cryptographically secure random bytes.
                rngCsp.GetBytes(randomBytes);
            }
            return randomBytes;
        }
    }
}
_

Die obige Klasse kann ganz einfach mit folgendem Code verwendet werden:

_using System;

namespace EncryptStringSample
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Please enter a password to use:");
            string password = Console.ReadLine();
            Console.WriteLine("Please enter a string to encrypt:");
            string plaintext = Console.ReadLine();
            Console.WriteLine("");

            Console.WriteLine("Your encrypted string is:");
            string encryptedstring = StringCipher.Encrypt(plaintext, password);
            Console.WriteLine(encryptedstring);
            Console.WriteLine("");

            Console.WriteLine("Your decrypted string is:");
            string decryptedstring = StringCipher.Decrypt(encryptedstring, password);
            Console.WriteLine(decryptedstring);
            Console.WriteLine("");

            Console.WriteLine("Press any key to exit...");
            Console.ReadLine();
        }
    }
}
_

(Sie können eine einfache VS2013-Musterlösung herunterladen (die einige Komponententests enthält) hier ).

UPDATE 23/Dec/2015: Die Liste der spezifischen Verbesserungen des Codes sind:

  • Es wurde ein dummer Fehler behoben, bei dem das Verschlüsseln und Entschlüsseln unterschiedlich war. Da sich der Mechanismus, mit dem Salt & IV-Werte generiert werden, geändert hat, ist keine Codierung mehr erforderlich.
  • Aufgrund der Salt/IV-Änderung ist der vorherige Codekommentar, der fälschlicherweise angibt, dass die UTF8-Codierung einer 16-Zeichen-Zeichenfolge 32 Byte ergibt, nicht mehr gültig (da keine Codierung mehr erforderlich ist).
  • Die Verwendung des ersetzten PBKDF1-Algorithmus wurde durch die Verwendung des moderneren PBKDF2-Algorithmus ersetzt.
  • Die Passwortableitung ist jetzt korrekt gesalzen, wohingegen sie zuvor überhaupt nicht gesalzen war (ein weiterer dummer Bug wurde beseitigt).
692
CraigTP
using System.IO;
using System.Text;
using System.Security.Cryptography;

public static class EncryptionHelper
{
    public static string Encrypt(string clearText)
    {
        string EncryptionKey = "abc123";
        byte[] clearBytes = Encoding.Unicode.GetBytes(clearText);
        using (Aes encryptor = Aes.Create())
        {
            Rfc2898DeriveBytes pdb = new Rfc2898DeriveBytes(EncryptionKey, new byte[] { 0x49, 0x76, 0x61, 0x6e, 0x20, 0x4d, 0x65, 0x64, 0x76, 0x65, 0x64, 0x65, 0x76 });
            encryptor.Key = pdb.GetBytes(32);
            encryptor.IV = pdb.GetBytes(16);
            using (MemoryStream ms = new MemoryStream())
            {
                using (CryptoStream cs = new CryptoStream(ms, encryptor.CreateEncryptor(), CryptoStreamMode.Write))
                {
                    cs.Write(clearBytes, 0, clearBytes.Length);
                    cs.Close();
                }
                clearText = Convert.ToBase64String(ms.ToArray());
            }
        }
        return clearText;
    }
    public static string Decrypt(string cipherText)
    {
        string EncryptionKey = "abc123";
        cipherText = cipherText.Replace(" ", "+");
        byte[] cipherBytes = Convert.FromBase64String(cipherText);
        using (Aes encryptor = Aes.Create())
        {
            Rfc2898DeriveBytes pdb = new Rfc2898DeriveBytes(EncryptionKey, new byte[] { 0x49, 0x76, 0x61, 0x6e, 0x20, 0x4d, 0x65, 0x64, 0x76, 0x65, 0x64, 0x65, 0x76 });
            encryptor.Key = pdb.GetBytes(32);
            encryptor.IV = pdb.GetBytes(16);
            using (MemoryStream ms = new MemoryStream())
            {
                using (CryptoStream cs = new CryptoStream(ms, encryptor.CreateDecryptor(), CryptoStreamMode.Write))
                {
                    cs.Write(cipherBytes, 0, cipherBytes.Length);
                    cs.Close();
                }
                cipherText = Encoding.Unicode.GetString(ms.ToArray());
            }
        }
        return cipherText;
    }
}
77
A Ghazal

Versuchen Sie diese Klasse:

public class DataEncryptor
{
    TripleDESCryptoServiceProvider symm;

    #region Factory
    public DataEncryptor()
    {
        this.symm = new TripleDESCryptoServiceProvider();
        this.symm.Padding = PaddingMode.PKCS7;
    }
    public DataEncryptor(TripleDESCryptoServiceProvider keys)
    {
        this.symm = keys;
    }

    public DataEncryptor(byte[] key, byte[] iv)
    {
        this.symm = new TripleDESCryptoServiceProvider();
        this.symm.Padding = PaddingMode.PKCS7;
        this.symm.Key = key;
        this.symm.IV = iv;
    }

    #endregion

    #region Properties
    public TripleDESCryptoServiceProvider Algorithm
    {
        get { return symm; }
        set { symm = value; }
    }
    public byte[] Key
    {
        get { return symm.Key; }
        set { symm.Key = value; }
    }
    public byte[] IV
    {
        get { return symm.IV; }
        set { symm.IV = value; }
    }

    #endregion

    #region Crypto

    public byte[] Encrypt(byte[] data) { return Encrypt(data, data.Length); }
    public byte[] Encrypt(byte[] data, int length)
    {
        try
        {
            // Create a MemoryStream.
            var ms = new MemoryStream();

            // Create a CryptoStream using the MemoryStream 
            // and the passed key and initialization vector (IV).
            var cs = new CryptoStream(ms,
                symm.CreateEncryptor(symm.Key, symm.IV),
                CryptoStreamMode.Write);

            // Write the byte array to the crypto stream and flush it.
            cs.Write(data, 0, length);
            cs.FlushFinalBlock();

            // Get an array of bytes from the 
            // MemoryStream that holds the 
            // encrypted data.
            byte[] ret = ms.ToArray();

            // Close the streams.
            cs.Close();
            ms.Close();

            // Return the encrypted buffer.
            return ret;
        }
        catch (CryptographicException ex)
        {
            Console.WriteLine("A cryptographic error occured: {0}", ex.Message);
        }
        return null;
    }

    public string EncryptString(string text)
    {
        return Convert.ToBase64String(Encrypt(Encoding.UTF8.GetBytes(text)));
    }

    public byte[] Decrypt(byte[] data) { return Decrypt(data, data.Length); }
    public byte[] Decrypt(byte[] data, int length)
    {
        try
        {
            // Create a new MemoryStream using the passed 
            // array of encrypted data.
            MemoryStream ms = new MemoryStream(data);

            // Create a CryptoStream using the MemoryStream 
            // and the passed key and initialization vector (IV).
            CryptoStream cs = new CryptoStream(ms,
                symm.CreateDecryptor(symm.Key, symm.IV),
                CryptoStreamMode.Read);

            // Create buffer to hold the decrypted data.
            byte[] result = new byte[length];

            // Read the decrypted data out of the crypto stream
            // and place it into the temporary buffer.
            cs.Read(result, 0, result.Length);
            return result;
        }
        catch (CryptographicException ex)
        {
            Console.WriteLine("A cryptographic error occured: {0}", ex.Message);
        }
        return null;
    }

    public string DecryptString(string data)
    {
        return Encoding.UTF8.GetString(Decrypt(Convert.FromBase64String(data))).TrimEnd('\0');
    }

    #endregion

}

und benutze es so:

string message="A very secret message here.";
DataEncryptor keys=new DataEncryptor();
string encr=keys.EncryptString(message);

// later
string actual=keys.DecryptString(encr);
28
ja72

Wenn Sie ein Kennwort speichern müssen und es verschlüsselt haben möchten, sollten Sie SecureString verwenden:

http://msdn.Microsoft.com/en-us/library/system.security.securestring.aspx

Für allgemeinere Anwendungen würde ich einen von FIPS zugelassenen Algorithmus wie Advanced Encryption Standard verwenden, der früher als Rijndael bekannt war. Auf dieser Seite finden Sie ein Implementierungsbeispiel:

http://msdn.Microsoft.com/en-us/library/system.security.cryptography.rijndael.aspx

19
Ulises

Wenn Sie auf ASP.NET Core abzielen, der RijndaelManaged noch nicht unterstützt, können Sie IDataProtectionProvider verwenden.

Konfigurieren Sie zunächst Ihre Anwendung für die Verwendung des Datenschutzes:

public class Startup
{
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddDataProtection();
    }
    // ...
}

Dann können Sie die Instanz IDataProtectionProvider einfügen und zum Ver-/Entschlüsseln von Daten verwenden:

public class MyService : IService
{
    private const string Purpose = "my protection purpose";
    private readonly IDataProtectionProvider _provider;

    public MyService(IDataProtectionProvider provider)
    {
        _provider = provider;
    }

    public string Encrypt(string plainText)
    {
        var protector = _provider.CreateProtector(Purpose);
        return protector.Protect(plainText);
    }

    public string Decrypt(string cipherText)
    {
        var protector = _provider.CreateProtector(Purpose);
        return protector.Unprotect(cipherText);
    }
}

Siehe dieser Artikel für weitere Details.

16
Sergey Kolodiy

Möglicherweise suchen Sie nach der Klasse ProtectedData, die Daten mit den Anmeldeinformationen des Benutzers verschlüsselt.

9
SLaks