441 votes

Une simple "obfuscation" de données bidirectionnelles non sécurisées ?

Je cherche une fonctionnalité d'obfuscation très simple (comme le cryptage et le décryptage, mais pas nécessairement sécurisée) pour certaines données. Ce n'est pas une mission critique. J'ai besoin de quelque chose qui permette aux personnes honnêtes de rester honnêtes, mais quelque chose d'un peu plus fort que le cryptage. ROT13 ou Base64 .

Je préfèrerais quelque chose qui est déjà inclus dans la .NET framework 2.0, de sorte que je n'ai pas à me soucier de dépendances externes.

Je ne veux vraiment pas avoir à m'embrouiller avec des clés publiques/privées, etc. Je ne connais pas grand-chose au cryptage, mais j'en sais assez pour savoir que tout ce que j'écrirais serait moins que sans valeur... En fait, je me tromperais probablement dans les maths et le rendrais facile à craquer.

3 votes

Salut Mark -- pas de problème. Je me suis senti mal de devoir refuser la réponse de richdiet, car j'ai effectivement utilisé sa solution et elle a très bien fonctionné. Cependant, j'ai continué à revenir ici pour lire les autres réponses, et la tienne est vraiment meilleure. Il n'y a pas de raison de dire aux gens d'utiliser quelque chose qui, bien qu'il fonctionne, n'est pas vraiment une bonne façon de faire quelque chose quand il y a une meilleure réponse disponible.

3 votes

Épargnez-vous des heures et utilisez HttpServerUtility.UrlTokenEn/Decode pour convertir les tableaux d'octets en une chaîne compatible avec l'url.

33 votes

+1 pour ne pas essayer de rouler votre propre conception intelligente. Vous ne connaissez peut-être pas grand-chose au cryptage, mais le fait que vous le sachiez vous place à des années-lumière de la plupart des développeurs que j'ai rencontrés et qui ne connaissent pas grand-chose au cryptage mais pensent pouvoir créer leur propre solution de toute façon.

473voto

Mark Brittingham Points 18970

Les autres réponses ici fonctionnent bien, mais AES est un algorithme de cryptage plus sûr et plus récent. Il s'agit d'une classe que j'ai obtenue il y a quelques années pour effectuer le cryptage AES et que j'ai modifiée au fil du temps pour la rendre plus conviviale pour les applications Web (par exemple, j'ai construit des méthodes de cryptage/décryptage qui fonctionnent avec des chaînes conviviales pour les URL). Il possède également les méthodes qui fonctionnent avec les tableaux d'octets.

REMARQUE : vous devez utiliser des valeurs différentes dans les tableaux Key (32 octets) et Vector (16 octets) ! Vous ne voudriez pas que quelqu'un découvre vos clés en supposant que vous avez utilisé ce code tel quel ! Tout ce que vous avez à faire est de changer certains des nombres (doivent être <= 255) dans les tableaux Key et Vector (j'ai laissé une valeur invalide dans le tableau Vector pour être sûr que vous le fassiez...). Vous pouvez utiliser https://www.random.org/bytes/ pour générer facilement un nouveau jeu :

Son utilisation est simple : il suffit d'instancier la classe et d'appeler (généralement) EncryptToString(string StringToEncrypt) et DecryptString(string StringToDecrypt) comme méthodes. Rien de plus facile (ou de plus sûr) une fois que vous avez mis en place cette classe.


using System;
using System.Data;
using System.Security.Cryptography;
using System.IO;

public class SimpleAES
{
    // Change these keys
    private byte[] Key = __Replace_Me__({ 123, 217, 19, 11, 24, 26, 85, 45, 114, 184, 27, 162, 37, 112, 222, 209, 241, 24, 175, 144, 173, 53, 196, 29, 24, 26, 17, 218, 131, 236, 53, 209 });

    // a hardcoded IV should not be used for production AES-CBC code
    // IVs should be unpredictable per ciphertext
    private byte[] Vector = __Replace_Me__({ 146, 64, 191, 111, 23, 3, 113, 119, 231, 121, 2521, 112, 79, 32, 114, 156 });

    private ICryptoTransform EncryptorTransform, DecryptorTransform;
    private System.Text.UTF8Encoding UTFEncoder;

    public SimpleAES()
    {
        //This is our encryption method
        RijndaelManaged rm = new RijndaelManaged();

        //Create an encryptor and a decryptor using our encryption method, key, and vector.
        EncryptorTransform = rm.CreateEncryptor(this.Key, this.Vector);
        DecryptorTransform = rm.CreateDecryptor(this.Key, this.Vector);

        //Used to translate bytes to text and vice versa
        UTFEncoder = new System.Text.UTF8Encoding();
    }

    /// -------------- Two Utility Methods (not used but may be useful) -----------
    /// Generates an encryption key.
    static public byte[] GenerateEncryptionKey()
    {
        //Generate a Key.
        RijndaelManaged rm = new RijndaelManaged();
        rm.GenerateKey();
        return rm.Key;
    }

    /// Generates a unique encryption vector
    static public byte[] GenerateEncryptionVector()
    {
        //Generate a Vector
        RijndaelManaged rm = new RijndaelManaged();
        rm.GenerateIV();
        return rm.IV;
    }

    /// ----------- The commonly used methods ------------------------------    
    /// Encrypt some text and return a string suitable for passing in a URL.
    public string EncryptToString(string TextValue)
    {
        return ByteArrToString(Encrypt(TextValue));
    }

    /// Encrypt some text and return an encrypted byte array.
    public byte[] Encrypt(string TextValue)
    {
        //Translates our text value into a byte array.
        Byte[] bytes = UTFEncoder.GetBytes(TextValue);

        //Used to stream the data in and out of the CryptoStream.
        MemoryStream memoryStream = new MemoryStream();

        /*
         * We will have to write the unencrypted bytes to the stream,
         * then read the encrypted result back from the stream.
         */
        #region Write the decrypted value to the encryption stream
        CryptoStream cs = new CryptoStream(memoryStream, EncryptorTransform, CryptoStreamMode.Write);
        cs.Write(bytes, 0, bytes.Length);
        cs.FlushFinalBlock();
        #endregion

        #region Read encrypted value back out of the stream
        memoryStream.Position = 0;
        byte[] encrypted = new byte[memoryStream.Length];
        memoryStream.Read(encrypted, 0, encrypted.Length);
        #endregion

        //Clean up.
        cs.Close();
        memoryStream.Close();

        return encrypted;
    }

    /// The other side: Decryption methods
    public string DecryptString(string EncryptedString)
    {
        return Decrypt(StrToByteArray(EncryptedString));
    }

    /// Decryption when working with byte arrays.    
    public string Decrypt(byte[] EncryptedValue)
    {
        #region Write the encrypted value to the decryption stream
        MemoryStream encryptedStream = new MemoryStream();
        CryptoStream decryptStream = new CryptoStream(encryptedStream, DecryptorTransform, CryptoStreamMode.Write);
        decryptStream.Write(EncryptedValue, 0, EncryptedValue.Length);
        decryptStream.FlushFinalBlock();
        #endregion

        #region Read the decrypted value from the stream.
        encryptedStream.Position = 0;
        Byte[] decryptedBytes = new Byte[encryptedStream.Length];
        encryptedStream.Read(decryptedBytes, 0, decryptedBytes.Length);
        encryptedStream.Close();
        #endregion
        return UTFEncoder.GetString(decryptedBytes);
    }

    /// Convert a string to a byte array.  NOTE: Normally we'd create a Byte Array from a string using an ASCII encoding (like so).
    //      System.Text.ASCIIEncoding encoding = new System.Text.ASCIIEncoding();
    //      return encoding.GetBytes(str);
    // However, this results in character values that cannot be passed in a URL.  So, instead, I just
    // lay out all of the byte values in a long string of numbers (three per - must pad numbers less than 100).
    public byte[] StrToByteArray(string str)
    {
        if (str.Length == 0)
            throw new Exception("Invalid string value in StrToByteArray");

        byte val;
        byte[] byteArr = new byte[str.Length / 3];
        int i = 0;
        int j = 0;
        do
        {
            val = byte.Parse(str.Substring(i, 3));
            byteArr[j++] = val;
            i += 3;
        }
        while (i < str.Length);
        return byteArr;
    }

    // Same comment as above.  Normally the conversion would use an ASCII encoding in the other direction:
    //      System.Text.ASCIIEncoding enc = new System.Text.ASCIIEncoding();
    //      return enc.GetString(byteArr);    
    public string ByteArrToString(byte[] byteArr)
    {
        byte val;
        string tempStr = "";
        for (int i = 0; i <= byteArr.GetUpperBound(0); i++)
        {
            val = byteArr[i];
            if (val < (byte)10)
                tempStr += "00" + val.ToString();
            else if (val < (byte)100)
                tempStr += "0" + val.ToString();
            else
                tempStr += val.ToString();
        }
        return tempStr;
    }
}

0 votes

Merci - c'est l'une de mes réponses préférées et je suis heureux que les gens en tirent profit. Certains pensent qu'il faut saler le début de la chaîne cryptée pour éviter la découverte du tableau vectoriel, mais cela dépend vraiment du degré de paranoïa que vous avez à l'égard des personnes qui essaient de décrypter vos chaînes et du fait qu'elles savent ce qui est crypté pour commencer.

3 votes

Le site using La police (ce n'est pas moi) pourrait être après vous pour une partie de l'utilisation du flux.

0 votes

Est-ce que ce serait pour s'assurer que le memoryStream est fermé ? J'utilise beaucoup plus le terme "utiliser" aujourd'hui que lorsque j'ai écrit cet algorithme, je comprends donc votre point de vue. Je ne veux pas changer cela sans le tester, cependant, donc cela pourrait prendre un peu de temps...

185voto

Mud Points 12084

J'ai nettoyé SimpleAES (ci-dessus) pour mon usage. J'ai corrigé les méthodes alambiquées de cryptage/décryptage ; j'ai séparé les méthodes d'encodage des tampons d'octets, des chaînes et des chaînes conviviales pour les URL ; j'ai utilisé des bibliothèques existantes pour l'encodage des URL.

Le code est petit, plus simple, plus rapide et le résultat est plus concis. Par exemple, johnsmith@gmail.com produit :

SimpleAES: "096114178117140150104121138042115022037019164188092040214235183167012211175176167001017163166152"
SimplerAES: "YHKydYyWaHmKKnMWJROkvFwo1uu3pwzTr7CnARGjppg%3d"

Code :

public class SimplerAES
{
    private static byte[] key = __Replace_Me__({ 123, 217, 19, 11, 24, 26, 85, 45, 114, 184, 27, 162, 37, 112, 222, 209, 241, 24, 175, 144, 173, 53, 196, 29, 24, 26, 17, 218, 131, 236, 53, 209 });

    // a hardcoded IV should not be used for production AES-CBC code
    // IVs should be unpredictable per ciphertext
    private static byte[] vector = __Replace_Me_({ 146, 64, 191, 111, 23, 3, 113, 119, 231, 121, 221, 112, 79, 32, 114, 156 });

    private ICryptoTransform encryptor, decryptor;
    private UTF8Encoding encoder;

    public SimplerAES()
    {
        RijndaelManaged rm = new RijndaelManaged();
        encryptor = rm.CreateEncryptor(key, vector);
        decryptor = rm.CreateDecryptor(key, vector);
        encoder = new UTF8Encoding();
    }

    public string Encrypt(string unencrypted)
    {
        return Convert.ToBase64String(Encrypt(encoder.GetBytes(unencrypted)));
    }

    public string Decrypt(string encrypted)
    {
        return encoder.GetString(Decrypt(Convert.FromBase64String(encrypted)));
    }

    public byte[] Encrypt(byte[] buffer)
    {
        return Transform(buffer, encryptor);
    }

    public byte[] Decrypt(byte[] buffer)
    {
        return Transform(buffer, decryptor);
    }

    protected byte[] Transform(byte[] buffer, ICryptoTransform transform)
    {
        MemoryStream stream = new MemoryStream();
        using (CryptoStream cs = new CryptoStream(stream, transform, CryptoStreamMode.Write))
        {
            cs.Write(buffer, 0, buffer.Length);
        }
        return stream.ToArray();
    }
}

2 votes

Lors du décodage, j'ai dû remplacer l'espace par + pour que cela fonctionne avec QueryString dans Chrome : (new SimplerAES()).Decrypt(Request.QueryString["myParam"].Replace(' ', '+')) ;

21 votes

N'utilisez jamais un vecteur d'initialisation constant, voir : crypto.stackexchange.com/questions/66/ pour plus d'informations à ce sujet. Au lieu de cela, générez un nouveau IV pour chaque cryptage et ajoutez-le au cryptotexte, c'est tellement mieux et pas si difficile.

1 votes

EncryptToUrl et DecryptFromUrl n'ont pas d'affaires nécessaires dans cette classe. Sinon, excellente réponse.

35voto

ZeroBugBounce Points 2367

Oui, ajoutez le System.Security importez l'ensemble System.Security.Cryptography espace de noms. Voici un exemple simple de chiffrement par un algorithme symétrique (DES) :

DESCryptoServiceProvider des = new DESCryptoServiceProvider();
des.GenerateKey();
byte[] key = des.Key; // save this!

ICryptoTransform encryptor = des.CreateEncryptor();
// encrypt
byte[] enc = encryptor.TransformFinalBlock(new byte[] { 1, 2, 3, 4 }, 0, 4);

ICryptoTransform decryptor = des.CreateDecryptor();

// decrypt
byte[] originalAgain = decryptor.TransformFinalBlock(enc, 0, enc.Length);
Debug.Assert(originalAgain[0] == 1);

5 votes

Il s'agit d'un chiffrement bidirectionnel compact et agréable. Le seul bémol est que DES n'est plus considéré comme une sécurité de pointe. Ce titre revient désormais à l'algorithme AES dont je parle ci-dessous.

0 votes

@richdiet. Je suis désolé de n'avoir pas accepté votre réponse. L'autre réponse avec 37+ votes car elle est plus actuelle. Merci pour votre réponse, car elle est toujours bonne.

14 votes

@MarkBrittingham : tout chiffrement par blocs sans fonction de chaînage de blocs, vecteur d'initialisation et rembourrage approprié est peu sûr. L'utilisation de DES est le problème le moins important de ce schéma.

12voto

Simon Points 11945

Une variante de la réponse Marks (excellent)

  • Ajouter des "en utilisant".
  • Rendre la classe IDisposable
  • Supprimez le code d'encodage de l'URL pour rendre l'exemple plus simple.
  • Ajouter un dispositif de test simple pour démontrer l'utilisation

J'espère que cela vous aidera

[TestFixture]
public class RijndaelHelperTests
{
    [Test]
    public void UseCase()
    {
        //These two values should not be hard coded in your code.
        byte[] key = {251, 9, 67, 117, 237, 158, 138, 150, 255, 97, 103, 128, 183, 65, 76, 161, 7, 79, 244, 225, 146, 180, 51, 123, 118, 167, 45, 10, 184, 181, 202, 190};
        byte[] vector = {214, 11, 221, 108, 210, 71, 14, 15, 151, 57, 241, 174, 177, 142, 115, 137};

        using (var rijndaelHelper = new RijndaelHelper(key, vector))
        {
            var encrypt = rijndaelHelper.Encrypt("StringToEncrypt");
            var decrypt = rijndaelHelper.Decrypt(encrypt);
            Assert.AreEqual("StringToEncrypt", decrypt);
        }
    }
}

public class RijndaelHelper : IDisposable
{
    Rijndael rijndael;
    UTF8Encoding encoding;

    public RijndaelHelper(byte[] key, byte[] vector)
    {
        encoding = new UTF8Encoding();
        rijndael = Rijndael.Create();
        rijndael.Key = key;
        rijndael.IV = vector;
    }

    public byte[] Encrypt(string valueToEncrypt)
    {
        var bytes = encoding.GetBytes(valueToEncrypt);
        using (var encryptor = rijndael.CreateEncryptor())
        using (var stream = new MemoryStream())
        using (var crypto = new CryptoStream(stream, encryptor, CryptoStreamMode.Write))
        {
            crypto.Write(bytes, 0, bytes.Length);
            crypto.FlushFinalBlock();
            stream.Position = 0;
            var encrypted = new byte[stream.Length];
            stream.Read(encrypted, 0, encrypted.Length);
            return encrypted;
        }
    }

    public string Decrypt(byte[] encryptedValue)
    {
        using (var decryptor = rijndael.CreateDecryptor())
        using (var stream = new MemoryStream())
        using (var crypto = new CryptoStream(stream, decryptor, CryptoStreamMode.Write))
        {
            crypto.Write(encryptedValue, 0, encryptedValue.Length);
            crypto.FlushFinalBlock();
            stream.Position = 0;
            var decryptedBytes = new Byte[stream.Length];
            stream.Read(decryptedBytes, 0, decryptedBytes.Length);
            return encoding.GetString(decryptedBytes);
        }
    }

    public void Dispose()
    {
        if (rijndael != null)
        {
            rijndael.Dispose();
        }
    }
}

0 votes

Bonne réponse. Dans la méthode Dispose, vous devez transformer rijndael en IDisposable ou vous obtiendrez une erreur de niveau de protection en appelant Dispose.

8 votes

N'utilisez jamais un Vecteur d'Initialisation constant, voir : crypto.stackexchange.com/questions/66/ pour plus d'informations sur le pourquoi. A la place, générez un nouveau IV pour chaque cryptage et ajoutez-le au cryptotexte, c'est tellement mieux et pas trop difficile.

0 votes

@TomHeard - comment faites-vous cela en C# ?

8voto

stalepretzel Points 4057

Des années plus tard, je suis revenu pour dire : Ne faites pas ça ! Voir Quel est le problème avec le cryptage XOR ? pour les détails.

Un cryptage bidirectionnel très simple et facile est le cryptage XOR.

  1. Trouvez un mot de passe. Disons que c'est mypass .

  2. Convertit le mot de passe en binaire (selon l'ASCII). Le mot de passe devient 01101101 01111001 01110000 01100001 01110011 01110011.

  3. Prenez le message que vous voulez coder. Convertissez-le en binaire, également.

  4. Regardez la longueur du message. Si la longueur du message est de 400 octets, transformez le mot de passe en une chaîne de 400 octets en le répétant encore et encore. Il deviendrait 01101101 01111001 01110000 01100001 01110011 01110011 01101101 01111001 01110000 01100001 01110011 01110011 01110011 01101101 01111001 01110000 01100001 01110011 01110011... (ou mypassmypassmypass... )

  5. XOR le message avec le mot de passe long.

  6. Envoyez le résultat.

  7. Une autre fois, XOR le message crypté avec le même mot de passe ( mypassmypassmypass... ).

  8. Voilà votre message !

10 votes

@Ryan Toutes les situations ne nécessitent pas des hachages cryptographiquement sûrs ou des chiffrages Rijndael. "Simple 2 way encryption" pourrait en fait signifier simple ce qui suggère xor ou même ROT13.

1 votes

@Ryan : AES avec clé de cryptage statique, pas de vecteur d'initialisation et pas de fonction de chaînage de blocs est juste un nom fantaisiste pour le cryptage XOR, vous utilisez juste un KDF très fantaisiste...

17 votes

Avertissement de sécurité : N'utilisez pas ce code Le chiffrement XOR avec une clé répétitive est trivialement craqué.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X