単純で安全でない双方向データの「難読化」?


426

一部のデータの非常に単純な難読化(暗号化や復号化など、必ずしも安全ではない)機能を探しています。ミッションクリティカルではありません。私は正直な人々を正直に保つために何かが必要ですが、ROT13Base64より少し強いものが必要です。

.NET Framework 2.0に既に含まれているものを使用したいので、外部の依存関係について心配する必要はありません。

私は本当に公開鍵/秘密鍵などをいじくり回す必要はありません。暗号化についてはあまり知りませんが、私が書いたものはすべて、価値がないとは言えないことを十分に知っています...実際、私はおそらく数学を台無しにして、クラックするのを簡単にするでしょう。


3
こんにちはマーク-問題ありません。私は実際に彼のソリューションを使用し、それがうまくいったので、私はリッチダイエットからの答えを受け入れないようにしなければならなかったのが気分が悪かった。しかし、私は他の答えを読むためにここに戻ってきました、そしてあなたの答えは本当に優れています。うまくいくものの、より良い答えが得られたときに何かをするのに本当に良い方法ではない何かを使うように人々に言う理由はありません。
Matt Dawdy、2010

3
時間を節約し、HttpServerUtility.UrlTokenEn / Decodeを使用して、バイト配列からURLフレンドリーな文字列に相互に変換します。
Praesagus 2010

32
+1は、独自の巧妙なデザインを導入しようとしないためです。暗号化についてはあまり知らないかもしれませんが、暗号化についてはあまり知らないが、とにかく独自のソリューションを作成できると思っているほとんどの開発者よりも、はるかに数年前に進んでいます。
ダイナ

6
注意:この質問の回答の多くは、認証されていない暗号化のみです。これは、攻撃者がアプリに気付かれずにデータを変更できることを意味します。他の深刻な脆弱性も引き起こします(オラクルのパディングによるキーなしの復号化など)。TL; DR:それで問題がなければ、答えにコードを使用しないでください。または、今言ったことを理解できません。
usr

36
この質問に対する単一の回答では、安全な暗号化について説明していません。暗号化でjbtuleの回答を使用し代わりに文字列を復号化します
CodesInChaos 2016年

回答:


471

ここでの他の回答は問題なく機能しますが、AESはより安全で最新の暗号化アルゴリズムです。これは、数年前にAES暗号化を実行するために取得したクラスであり、時間の経過とともに変更されてWebアプリケーションにより使いやすくなりました(たとえば、URLフレンドリーな文字列で動作するEncrypt / Decryptメソッドを作成しました)。また、バイト配列を操作するメソッドもあります。

注:キー(32バイト)配列とベクター(16バイト)配列には異なる値を使用する必要があります。このコードをそのまま使用したと想定して、誰かに自分のキーを理解してほしくないでしょう。あなたがしなければならないのは、Key配列とVector配列のいくつかの数値(255以下でなければならない)を変更することだけです(これを確実にするために、Vector配列に1つの無効な値を残しました...)。https://www.random.org/bytes/を使用して、新しいセットを簡単に生成できます。

使い方は簡単です。クラスをインスタンス化し、メソッドとして(通常)EncryptToString(string StringToEncrypt)とDecryptString(string StringToDecrypt)を呼び出すだけです。このクラスを配置すると、これ以上簡単にはなりません(またはより安全になります)。


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;
    }
}

53
@AndyMcKenna-マークが2番目の段落で注記しているように、配列の値を変更するために意図的に行われます。
Pauk

42
このようなIVは使用しないでください。特定の2つのメッセージについて、それらは同じキーと同じIVで暗号化されるべきではありません。IVはメッセージごとにランダムで、暗号ストリームの前に付加され、復号化の前に読み取られる必要があります。crypto.stackexchange.com/a/82/1934
jbtule

30
各メッセージにランダムIVを使用することは、珍しいことでも新しいことでもありません。重要であり、アルゴリズムの設計の一部です。すべてのメッセージに予測可能なIVを使用することは、永続化する必要のない一般的な暗号ミスです。
jbtule 2012

14
また、モードとしてCBCを使用すると、オラクル攻撃のパディングに対して脆弱になる可能性が高いことにも注意してください。認証された暗号化を使用し、可能な限り、自分で暗号化を実装しないでください
Stephen Touset 2013年

57
セキュリティ警告:このコードは使用しないでください受け入れられた回答であるにもかかわらず、上記のコメントで言及されている深刻なセキュリティ問題があり、著者は8年間無視し続けてきました。
jbtule 2017年

176

SimpleAES(上記)をクリーンアップして使用しました。複雑な暗号化/復号化メソッドを修正しました。バイトバッファー、文字列、およびURLフレンドリーな文字列をエンコードするための個別のメソッド。URLエンコーディングに既存のライブラリを利用しました。

コードは小さく、シンプルで、高速で、出力はより簡潔です。たとえば、以下をjohnsmith@gmail.com生成します。

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

コード:

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
デコードするとき、ChromeでQueryStringを使用できるように、スペースを+に置き換える必要がありました:(new SimplerAES())。Decrypt(Request.QueryString ["myParam"]。Replace( ''、 '+'));
live-love

20
定数の初期化ベクトルを使用しないでください。理由の詳細については、crypto.stackexchange.com / questions / 66 / を参照してください。代わりに、暗号化ごとに新しいIVを生成し、それを暗号文に追加します。
トムハード

2
次のように、このソリューションのEncryptToUrlメソッドの出力(または一般にUrlEncodedベース64文字列の使用)は、URLパス(クエリ文字列ではない)の一部として使用されると、IIS 7ではデフォルトで機能しません。 IIS 7のセキュリティ設定によるASP.NET MVCルート。詳しくは、stackoverflow.com
Jon Schneider

5
@TomHeard上記のコードを使用して、どうやってそれを行うのですか?
MKII、2015

26
セキュリティ警告:このコードは使用しないでください @TomHeardによるコメントを参照してください
jbtule

36

はい、System.Securityアセンブリを追加し、System.Security.Cryptography名前空間をインポートします。次に、対称(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
これは、優れたコンパクトな双方向暗号化です。唯一の注意点は、DESが最先端のセキュリティと見なされなくなったことです。このタイトルは、以下で説明するAESアルゴリズムに適用されます。
マークブリッティンガム

@richdiet。すみません、あなたの答えを受け入れませんでした。もう1つは37票以上の回答です。これは最新の投票だからです。それはまだ良いので、あなたの答えをありがとう。
Matt Dawdy、2010

14
@MarkBrittingham:ブロックチェーン機能、初期化ベクトル、適切なパディングのないブロック暗号は安全ではありません。この方式では、DESを使用することが最も重要な問題です。
Hubert Kario 2012

2
では、キーはどこで使用されますか?
アレックス・

22
セキュリティ警告:このコードは使用しないでください @HubertKarioによるコメントを参照してください
jbtule

28

暗号化された文字列内に渡されるランダムなIVを追加することにより、MudのSimplerAESを改善したと私が付け加えたと思います。同じ文字列を暗号化すると毎回異なる出力になるため、これにより暗号化が向上します。

public class StringEncryption
{
    private readonly Random random;
    private readonly byte[] key;
    private readonly RijndaelManaged rm;
    private readonly UTF8Encoding encoder;

    public StringEncryption()
    {
        this.random = new Random();
        this.rm = new RijndaelManaged();
        this.encoder = new UTF8Encoding();
        this.key = Convert.FromBase64String("Your+Secret+Static+Encryption+Key+Goes+Here=");
    }

    public string Encrypt(string unencrypted)
    {
        var vector = new byte[16];
        this.random.NextBytes(vector);
        var cryptogram = vector.Concat(this.Encrypt(this.encoder.GetBytes(unencrypted), vector));
        return Convert.ToBase64String(cryptogram.ToArray());
    }

    public string Decrypt(string encrypted)
    {
        var cryptogram = Convert.FromBase64String(encrypted);
        if (cryptogram.Length < 17)
        {
            throw new ArgumentException("Not a valid encrypted string", "encrypted");
        }

        var vector = cryptogram.Take(16).ToArray();
        var buffer = cryptogram.Skip(16).ToArray();
        return this.encoder.GetString(this.Decrypt(buffer, vector));
    }

    private byte[] Encrypt(byte[] buffer, byte[] vector)
    {
        var encryptor = this.rm.CreateEncryptor(this.key, vector);
        return this.Transform(buffer, encryptor);
    }

    private byte[] Decrypt(byte[] buffer, byte[] vector)
    {
        var decryptor = this.rm.CreateDecryptor(this.key, vector);
        return this.Transform(buffer, decryptor);
    }

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

        return stream.ToArray();
    }
}

そしてボーナスユニットテスト

[Test]
public void EncryptDecrypt()
{
    // Arrange
    var subject = new StringEncryption();
    var originalString = "Testing123!£$";

    // Act
    var encryptedString1 = subject.Encrypt(originalString);
    var encryptedString2 = subject.Encrypt(originalString);
    var decryptedString1 = subject.Decrypt(encryptedString1);
    var decryptedString2 = subject.Decrypt(encryptedString2);

    // Assert
    Assert.AreEqual(originalString, decryptedString1, "Decrypted string should match original string");
    Assert.AreEqual(originalString, decryptedString2, "Decrypted string should match original string");
    Assert.AreNotEqual(originalString, encryptedString1, "Encrypted string should not match original string");
    Assert.AreNotEqual(encryptedString1, encryptedString2, "String should never be encrypted the same twice");
}

11
1)System.RandomRNGとして使用しないでください。2)これは、選択された暗号文攻撃(特にパディングオラクル)に対して完全に破られています
CodesInChaos '20

21
セキュリティ警告:このコード使用しないでください @CodesInChaosによる上記のコメントを参照してください
jbtule

@jbtuleは、暗号化だけを必要とせず、攻撃を警戒しないすべての人に誤解を与えないでください。提案が必要な場合は、注文しないでください。
Virbhadrasinh 2017年

@Virbhadrasinh私の側に誤解はありません、実際それは全く逆です。AESを使用する場合、正しく使用することは非常に重要であり、誤って使用し、重要なものに使用しなくても大丈夫であると言っていると誤解されます。
jbtule 2017年

1
@Corey怒鳴らないで、スタックオーバーフローの回答でセキュリティ問題に対処するためのベストプラクティスに従っていました。リンクが必要な場合は、質問のコメントに投稿されています。しかし、私はあなたのためにここにそれを置くとしてもよstackoverflow.com/a/10366194/637783
jbtule

12

マークのバリエーション(優れた)回答

  • 「使用」を追加
  • クラスをIDisposableにする
  • 例を簡単にするために、URLエンコードコードを削除します。
  • 簡単なテストフィクスチャを追加して、使用方法を示します

お役に立てれば

[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();
        }
    }
}

いい答えです。Disposeメソッドの1つは、rijndaelをIDisposableにキャストする必要があるか、Disposeを呼び出すことによって保護レベルエラーが発生する
John ClearZ '25

8
定数の初期化ベクトルを使用しないでください。理由の詳細については、crypto.stackexchange.com / questions / 66 /…を参照してください。代わりに、暗号化ごとに新しいIVを生成し、それを暗号文に追加します。
トム・ハード

5
@Chalky暗号化では、Rijndaelクラスを使用してランダムなIVを生成し(msdn.microsoft.com/en-us/library/…)、暗号化を実行してから、IVプロパティを使用してRijndaelインスタンスからIVを取得します。次に、暗号化テキストにプリペンド(または、追加、復号化が同じ側からそれを取得する限り機能します)します。復号化すると、受信したデータからIVをプルし(IVプロパティのサイズは、BlockSizeプロパティを8で割った値と同じです)、復号化する前に復号化インスタンスに渡します。
トムハード

2
@Chalky IVは秘密である必要はなく、送信されるメッセージごとに一意である必要があることに注意してください。
トムハード

20
セキュリティ警告:このコードは使用しないでください上記の@TomHeardのコメントを参照してください
jbtule

8

[編集]数年後、私は言いに戻ってきました:これをしないでください! 参照してくださいXOR暗号化で何が間違っているの?詳細については。

非常にシンプルで簡単な双方向暗号化はXOR暗号化です。

  1. パスワードを考え出す。それをしましょうmypass
  2. パスワードを(ASCIIに従って)バイナリに変換します。パスワードは01101101 01111001 01110000 01100001 01110011 01110011になります。
  3. エンコードするメッセージを受け取ります。それもバイナリに変換します。
  4. メッセージの長さを見てください。メッセージの長さが400バイトの場合は、パスワードを繰り返して400バイトの文字列にします。01101101 01111001 01110000 01100001 01110011 01110011 01101101 01111001 01110000 01100001 01110011 01110011 01101101 01111001 01110000 01100001 01110011 01110011 ...(またはmypassmypassmypass...
  5. メッセージに長いパスワードをXORします。
  6. 結果を送信します。
  7. もう一度、同じパスワードで暗号化されたメッセージをXORします(mypassmypassmypass...)。
  8. あなたのメッセージがあります!

10
@Ryanすべての状況で暗号的に安全なハッシュまたはRijndael暗号が必要なわけではありません。「単純な2方向の暗号化」は、実際には単純であることを意味する可能性があり、xorまたはROT13を示唆しています。

1
@ライアン:静的暗号化キー、初期化ベクトルなし、ブロックチェーン機能なしのAESは、XOR暗号化のファンシーな名前であり、本当にファンシーなKDFを使用している...
Hubert

17
セキュリティ警告:このコードは使用しないでください反復キーでのXOR暗号化は簡単に解読されます。
jbtule 2017年

7

いくつかの回答とコメントから、私が見つけたものを組み合わせました。

  • 暗号化テキストの前に付加されたランダムな初期化ベクトル(@jbtule)
  • MemoryStream(@RenniePet)の代わりにTransformFinalBlock()を使用します
  • 災害のコピーと貼り付けを回避するための事前入力されたキーはありません
  • 適切な処分とパターンの使用

コード:

/// <summary>
/// Simple encryption/decryption using a random initialization vector
/// and prepending it to the crypto text.
/// </summary>
/// <remarks>Based on multiple answers in http://stackoverflow.com/questions/165808/simple-two-way-encryption-for-c-sharp </remarks>
public class SimpleAes : IDisposable
{
    /// <summary>
    ///     Initialization vector length in bytes.
    /// </summary>
    private const int IvBytes = 16;

    /// <summary>
    ///     Must be exactly 16, 24 or 32 bytes long.
    /// </summary>
    private static readonly byte[] Key = Convert.FromBase64String("FILL ME WITH 24 (2 pad chars), 32 OR 44 (1 pad char) RANDOM CHARS"); // Base64 has a blowup of four-thirds (33%)

    private readonly UTF8Encoding _encoder;
    private readonly ICryptoTransform _encryptor;
    private readonly RijndaelManaged _rijndael;

    public SimpleAes()
    {
        _rijndael = new RijndaelManaged {Key = Key};
        _rijndael.GenerateIV();
        _encryptor = _rijndael.CreateEncryptor();
        _encoder = new UTF8Encoding();
    }

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

    public void Dispose()
    {
        _rijndael.Dispose();
        _encryptor.Dispose();
    }

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

    private byte[] Decrypt(byte[] buffer)
    {
        // IV is prepended to cryptotext
        byte[] iv = buffer.Take(IvBytes).ToArray();
        using (ICryptoTransform decryptor = _rijndael.CreateDecryptor(_rijndael.Key, iv))
        {
            return decryptor.TransformFinalBlock(buffer, IvBytes, buffer.Length - IvBytes);
        }
    }

    private byte[] Encrypt(byte[] buffer)
    {
        // Prepend cryptotext with IV
        byte [] inputBuffer = _encryptor.TransformFinalBlock(buffer, 0, buffer.Length); 
        return _rijndael.IV.Concat(inputBuffer).ToArray();
    }
}

2015-07-18の更新:@bpsilverおよび@EvereqのコメントによるプライベートEncrypt()メソッドの誤りを修正しました。IVは誤って暗号化されていましたが、Decrypt()で期待されるようにクリアテキストで付加されています。


inputBuffer全体をIVを付加して暗号化する必要があります。そうしないと、暗号化する文字列の最初の16文字が失われます。あなたのコードを読む必要がありますので、:return _encryptor.TransformFinalBlock(inputBuffer, 0, inputBuffer.Length);
bpsilver

2
その場合:byte [] inputBuffer = _encryptor.TransformFinalBlock(buffer, 0, buffer.Length); return _rijndael.IV.Concat(inputBuffer).ToArray();
bpsilver

1
それは現在の実装と同じことでしょうか?
アンギュラセン

1
「16、24、または32文字で埋める」は、ベース64デコードのではありません。そして、キーはランダムでなければなりません。本当にランダム。
Maarten Bodewes

1
@bpsilverが正しいことに気付き、提供されたコードは彼の修正なしでは機能しません。encryptメソッドはIV なしで暗号化されたデータ返します(最初にIVを入力バッファーに追加しますが、次に暗号化してそれなしでデータを返します)。したがって、可能であれば、彼のコードで回答を更新してください。(注:私は文字列ではなく、byte []パラメータを持つメソッドのみをテストします)。ありがとう!
Evereq

6

単純な暗号化だけが必要な場合(つまり、断固としたクラッカーが侵入する可能性はあるが、ほとんどのカジュアルユーザーをロックアウトする場合)、次のように、同じ長さの2つのパスフレーズを選択するだけです。

deoxyribonucleicacid
while (x>0) { x-- };

そして、それらの両方でデータをxorします(必要に応じてパスフレーズをループします)(a)。例えば:

1111-2222-3333-4444-5555-6666-7777
deoxyribonucleicaciddeoxyribonucle
while (x>0) { x-- };while (x>0) { 

バイナリを検索する人は、DNA文字列がキーであると考えるかもしれませんが、Cコードがバイナリで保存された初期化されていないメモリ以外のものであるとは考えにくいでしょう。


(a)これは非常に単純な暗号化であり、定義によっては、暗号化とはまったく見なされない場合があることに注意してください(暗号化の目的は不正アクセスを防ぐだけでなく、不正アクセスを防ぐことです)。もちろん、最も強力な暗号化であっても、誰かが鋼管でキーホルダーの上に立っていると安全ではありません。

最初の文で述べたように、これはカジュアルな攻撃者が次に進むのを十分に困難にする手段です。それはあなたの家の強盗を防ぐことに似ています-それを難攻不落にする必要はありません、あなたはそれを隣の家よりも妊娠しにくくする必要があります:-)


3
面白いアイデア。バイナリのソースコードを「信じる」かどうかはわかりませんが、パスフレーズとしてエラーメッセージを使用するようにアイデアを適応させるのはどうでしょうか。
Jon Skeet、

1
私は、アプリケーション(エラーメッセージなど)に既に存在するクリアテキスト文字列のmd5ハッシュを使用することを好みます。
Treb

2
なぜ同じ長さにする必要があるのですか?長さが異なるほうが実際には良いようです。このように、有効なXORオペランドの長さは、単にlength1(= length2)ではなく、LCM(length1、length2)になります。もちろん、長さが比較的素数であれば、どちらがlength1 * length2になります。
Fantius、2011年

15
セキュリティ警告:このコードは使用しないでください反復キーXORは、暗号化されるデータについての一般的な知識がいくつかあるだけで簡単に解読できます。
jbtule 2017年

3
@jbtule、あなたが質問を読んだら、もっと安全な暗号化は決して必要ではないことに気付くでしょう。具体的には、「単純な暗号化」、「ミッションクリティカルではない」、「正直な人々を正直に保つ」への言及。また、最初の段落を読んで、断固とした攻撃者をロックアウトしないという事実を明確に説明する必要があります。
paxdiablo

5

暗号化は簡単です。他の人が指摘したように、System.Security.Cryptography名前空間には、すべての作業を行うクラスがあります。自社製のソリューションではなく、それらを使用してください。

しかし、復号化も簡単です。あなたが抱えている問題は、暗号化アルゴリズムではなく、復号化に使用されるキーへのアクセスを保護することです。

次のいずれかの解決策を使用します。

  • CurrentUserスコープでProtectedDataクラスを使用するDPAPI キーを気にする必要がないので、これは簡単です。データは同じユーザーのみが復号化できるため、ユーザーまたはマシン間でデータを共有することはできません。

  • LocalMachineスコープを持つProtectedDataクラスを使用するDPAPI。たとえば、単一の安全なサーバー上の構成データを保護するのに適しています。しかし、マシンにログインできる人なら誰でも暗号化できるので、サーバーが安全でない限りダメです。

  • 対称アルゴリズム。どのアルゴリズムを使用するかを気にしない場合は、通常、静的なSymmetricAlgorithm.Create()メソッドを使用します(実際、デフォルトではRijndaelです)。この場合、何らかの方法でキーを保護する必要があります。たとえば、何らかの方法で難読化して、コードで非表示にすることができます。ただし、コードを逆コンパイルできるほど賢い人ならだれでもキーを見つけられることに注意してください。


5

上記の解決策はどれも私のような単純なものではないので、私は自分の解決策を投稿したかった。どう考えているか教えてください:

 // This will return an encrypted string based on the unencrypted parameter
 public static string Encrypt(this string DecryptedValue)
 {
      HttpServerUtility.UrlTokenEncode(MachineKey.Protect(Encoding.UTF8.GetBytes(DecryptedValue.Trim())));
 }

 // This will return an unencrypted string based on the parameter
 public static string Decrypt(this string EncryptedValue)
 {
      Encoding.UTF8.GetString(MachineKey.Unprotect(HttpServerUtility.UrlTokenDecode(EncryptedValue)));
 }

オプション

これは、値の暗号化に使用されるサーバーのMachineKeyが、値の復号化に使用されるものと同じであることを前提としています。必要に応じて、Web.configで静的なMachineKeyを指定して、アプリケーションの実行場所(開発サーバーと本番サーバーなど)に関係なく、アプリケーションがデータを復号化/暗号化できるようにすることができます。これらの手順に従って、静的マシンキーを生成できます


この方法はASP.NETアプリでのみ使用できます。
Feru

2

名前空間System.Security.CryptographyにはTripleDESCryptoServiceProviderおよびRijndaelManagedクラスが含まれています

System.Securityアセンブリへの参照を追加することを忘れないでください。


8
私が反対票を投じたわけではありませんが、投票時に質問の年齢が問題になるのはなぜですか?
user247702 14

2

System.Security.Cryptographyで TripleDESCryptoServiceProviderを使用する:

public static class CryptoHelper
{
    private const string Key = "MyHashString";
    private static TripleDESCryptoServiceProvider GetCryproProvider()
    {
        var md5 = new MD5CryptoServiceProvider();
        var key = md5.ComputeHash(Encoding.UTF8.GetBytes(Key));
        return new TripleDESCryptoServiceProvider() { Key = key, Mode = CipherMode.ECB, Padding = PaddingMode.PKCS7 };
    }

    public static string Encrypt(string plainString)
    {
        var data = Encoding.UTF8.GetBytes(plainString);
        var tripleDes = GetCryproProvider();
        var transform = tripleDes.CreateEncryptor();
        var resultsByteArray = transform.TransformFinalBlock(data, 0, data.Length);
        return Convert.ToBase64String(resultsByteArray);
    }

    public static string Decrypt(string encryptedString)
    {
        var data = Convert.FromBase64String(encryptedString);
        var tripleDes = GetCryproProvider();
        var transform = tripleDes.CreateDecryptor();
        var resultsByteArray = transform.TransformFinalBlock(data, 0, data.Length);
        return Encoding.UTF8.GetString(resultsByteArray);
    }
}

1

私はこれを変更しました

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;
}

これに:

    public string ByteArrToString(byte[] byteArr)
    {
        string temp = "";
        foreach (byte b in byteArr)
            temp += b.ToString().PadLeft(3, '0');
        return temp;
    }

1

この例では、組み込みの.Net暗号化ライブラリを使用して、Advanced Encryption Standard(AES)を使用する方法を示します。

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

namespace Aes_Example
{
    class AesExample
    {
        public static void Main()
        {
            try
            {

                string original = "Here is some data to encrypt!";

                // Create a new instance of the Aes
                // class.  This generates a new key and initialization 
                // vector (IV).
                using (Aes myAes = Aes.Create())
                {

                    // Encrypt the string to an array of bytes.
                    byte[] encrypted = EncryptStringToBytes_Aes(original, myAes.Key, myAes.IV);

                    // Decrypt the bytes to a string.
                    string roundtrip = DecryptStringFromBytes_Aes(encrypted, myAes.Key, myAes.IV);

                    //Display the original data and the decrypted data.
                    Console.WriteLine("Original:   {0}", original);
                    Console.WriteLine("Round Trip: {0}", roundtrip);
                }

            }
            catch (Exception e)
            {
                Console.WriteLine("Error: {0}", e.Message);
            }
        }
        static byte[] EncryptStringToBytes_Aes(string plainText, byte[] Key,byte[] IV)
        {
            // Check arguments.
            if (plainText == null || plainText.Length <= 0)
                throw new ArgumentNullException("plainText");
            if (Key == null || Key.Length <= 0)
                throw new ArgumentNullException("Key");
            if (IV == null || IV.Length <= 0)
                throw new ArgumentNullException("Key");
            byte[] encrypted;
            // Create an Aes object
            // with the specified key and IV.
            using (Aes aesAlg = Aes.Create())
            {
                aesAlg.Key = Key;
                aesAlg.IV = IV;

                // Create a decrytor to perform the stream transform.
                ICryptoTransform encryptor = aesAlg.CreateEncryptor(aesAlg.Key, aesAlg.IV);

                // Create the streams used for encryption.
                using (MemoryStream msEncrypt = new MemoryStream())
                {
                    using (CryptoStream csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write))
                    {
                        using (StreamWriter swEncrypt = new StreamWriter(csEncrypt))
                        {

                            //Write all data to the stream.
                            swEncrypt.Write(plainText);
                        }
                        encrypted = msEncrypt.ToArray();
                    }
                }
            }


            // Return the encrypted bytes from the memory stream.
            return encrypted;

        }

        static string DecryptStringFromBytes_Aes(byte[] cipherText, byte[] Key, byte[] IV)
        {
            // Check arguments.
            if (cipherText == null || cipherText.Length <= 0)
                throw new ArgumentNullException("cipherText");
            if (Key == null || Key.Length <= 0)
                throw new ArgumentNullException("Key");
            if (IV == null || IV.Length <= 0)
                throw new ArgumentNullException("Key");

            // Declare the string used to hold
            // the decrypted text.
            string plaintext = null;

            // Create an Aes object
            // with the specified key and IV.
            using (Aes aesAlg = Aes.Create())
            {
                aesAlg.Key = Key;
                aesAlg.IV = IV;

                // Create a decrytor to perform the stream transform.
                ICryptoTransform decryptor = aesAlg.CreateDecryptor(aesAlg.Key, aesAlg.IV);

                // Create the streams used for decryption.
                using (MemoryStream msDecrypt = new MemoryStream(cipherText))
                {
                    using (CryptoStream csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read))
                    {
                        using (StreamReader srDecrypt = new StreamReader(csDecrypt))
                        {

                            // Read the decrypted bytes from the decrypting stream
                            // and place them in a string.
                            plaintext = srDecrypt.ReadToEnd();
                        }
                    }
                }

            }

            return plaintext;

        }
    }
}


0

私はマークブリッティンガムの承認された回答を使用してきましたが、それは非常に役立ちました。最近、暗号化されたテキストを別の組織に送信する必要があり、そこで問題が発生しました。OPはこれらのオプションを必要としませんが、これはよくある質問なので、私の変更(EncryptおよびここDecryptから借用し関数)を投稿しています

  1. メッセージごとに異なるIV-16進数を取得する前に、IVバイトを暗号化バイトに連結します。もちろん、これは暗号文を受信する当事者に伝える必要のある規則です。
  2. 2つのコンストラクターを許可-1つはデフォルトRijndaelManaged値用、もう1つはプロパティ値を指定できるコンストラクター(暗号化と復号化の当事者間の相互合意に基づく)

これがクラスです(最後のテストサンプル)。

/// <summary>
/// Based on https://msdn.microsoft.com/en-us/library/system.security.cryptography.rijndaelmanaged(v=vs.110).aspx
/// Uses UTF8 Encoding
///  http://security.stackexchange.com/a/90850
/// </summary>
public class AnotherAES : IDisposable
{
    private RijndaelManaged rijn;

    /// <summary>
    /// Initialize algo with key, block size, key size, padding mode and cipher mode to be known.
    /// </summary>
    /// <param name="key">ASCII key to be used for encryption or decryption</param>
    /// <param name="blockSize">block size to use for AES algorithm. 128, 192 or 256 bits</param>
    /// <param name="keySize">key length to use for AES algorithm. 128, 192, or 256 bits</param>
    /// <param name="paddingMode"></param>
    /// <param name="cipherMode"></param>
    public AnotherAES(string key, int blockSize, int keySize, PaddingMode paddingMode, CipherMode cipherMode)
    {
        rijn = new RijndaelManaged();
        rijn.Key = Encoding.UTF8.GetBytes(key);
        rijn.BlockSize = blockSize;
        rijn.KeySize = keySize;
        rijn.Padding = paddingMode;
        rijn.Mode = cipherMode;
    }

    /// <summary>
    /// Initialize algo just with key
    /// Defaults for RijndaelManaged class: 
    /// Block Size: 256 bits (32 bytes)
    /// Key Size: 128 bits (16 bytes)
    /// Padding Mode: PKCS7
    /// Cipher Mode: CBC
    /// </summary>
    /// <param name="key"></param>
    public AnotherAES(string key)
    {
        rijn = new RijndaelManaged();
        byte[] keyArray = Encoding.UTF8.GetBytes(key);
        rijn.Key = keyArray;
    }

    /// <summary>
    /// Based on https://msdn.microsoft.com/en-us/library/system.security.cryptography.rijndaelmanaged(v=vs.110).aspx
    /// Encrypt a string using RijndaelManaged encryptor.
    /// </summary>
    /// <param name="plainText">string to be encrypted</param>
    /// <param name="IV">initialization vector to be used by crypto algorithm</param>
    /// <returns></returns>
    public byte[] Encrypt(string plainText, byte[] IV)
    {
        if (rijn == null)
            throw new ArgumentNullException("Provider not initialized");

        // Check arguments.
        if (plainText == null || plainText.Length <= 0)
            throw new ArgumentNullException("plainText cannot be null or empty");
        if (IV == null || IV.Length <= 0)
            throw new ArgumentNullException("IV cannot be null or empty");
        byte[] encrypted;

        // Create a decrytor to perform the stream transform.
        using (ICryptoTransform encryptor = rijn.CreateEncryptor(rijn.Key, IV))
        {
            // Create the streams used for encryption.
            using (MemoryStream msEncrypt = new MemoryStream())
            {
                using (CryptoStream csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write))
                {
                    using (StreamWriter swEncrypt = new StreamWriter(csEncrypt))
                    {
                        //Write all data to the stream.
                        swEncrypt.Write(plainText);
                    }
                    encrypted = msEncrypt.ToArray();
                }
            }
        }
        // Return the encrypted bytes from the memory stream.
        return encrypted;
    }//end EncryptStringToBytes

    /// <summary>
    /// Based on https://msdn.microsoft.com/en-us/library/system.security.cryptography.rijndaelmanaged(v=vs.110).aspx
    /// </summary>
    /// <param name="cipherText">bytes to be decrypted back to plaintext</param>
    /// <param name="IV">initialization vector used to encrypt the bytes</param>
    /// <returns></returns>
    public string Decrypt(byte[] cipherText, byte[] IV)
    {
        if (rijn == null)
            throw new ArgumentNullException("Provider not initialized");

        // Check arguments.
        if (cipherText == null || cipherText.Length <= 0)
            throw new ArgumentNullException("cipherText cannot be null or empty");
        if (IV == null || IV.Length <= 0)
            throw new ArgumentNullException("IV cannot be null or empty");

        // Declare the string used to hold the decrypted text.
        string plaintext = null;

        // Create a decrytor to perform the stream transform.
        using (ICryptoTransform decryptor = rijn.CreateDecryptor(rijn.Key, IV))
        {
            // Create the streams used for decryption.
            using (MemoryStream msDecrypt = new MemoryStream(cipherText))
            {
                using (CryptoStream csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read))
                {
                    using (StreamReader srDecrypt = new StreamReader(csDecrypt))
                    {
                        // Read the decrypted bytes from the decrypting stream and place them in a string.
                        plaintext = srDecrypt.ReadToEnd();
                    }
                }
            }
        }

        return plaintext;
    }//end DecryptStringFromBytes

    /// <summary>
    /// Generates a unique encryption vector using RijndaelManaged.GenerateIV() method
    /// </summary>
    /// <returns></returns>
    public byte[] GenerateEncryptionVector()
    {
        if (rijn == null)
            throw new ArgumentNullException("Provider not initialized");

        //Generate a Vector
        rijn.GenerateIV();
        return rijn.IV;
    }//end GenerateEncryptionVector


    /// <summary>
    /// Based on https://stackoverflow.com/a/1344255
    /// Generate a unique string given number of bytes required.
    /// This string can be used as IV. IV byte size should be equal to cipher-block byte size. 
    /// Allows seeing IV in plaintext so it can be passed along a url or some message.
    /// </summary>
    /// <param name="numBytes"></param>
    /// <returns></returns>
    public static string GetUniqueString(int numBytes)
    {
        char[] chars = new char[62];
        chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890".ToCharArray();
        byte[] data = new byte[1];
        using (RNGCryptoServiceProvider crypto = new RNGCryptoServiceProvider())
        {
            data = new byte[numBytes];
            crypto.GetBytes(data);
        }
        StringBuilder result = new StringBuilder(numBytes);
        foreach (byte b in data)
        {
            result.Append(chars[b % (chars.Length)]);
        }
        return result.ToString();
    }//end GetUniqueKey()

    /// <summary>
    /// Converts a string to byte array. Useful when converting back hex string which was originally formed from bytes.
    /// </summary>
    /// <param name="hex"></param>
    /// <returns></returns>
    public static byte[] StringToByteArray(String hex)
    {
        int NumberChars = hex.Length;
        byte[] bytes = new byte[NumberChars / 2];
        for (int i = 0; i < NumberChars; i += 2)
            bytes[i / 2] = Convert.ToByte(hex.Substring(i, 2), 16);
        return bytes;
    }//end StringToByteArray

    /// <summary>
    /// Dispose RijndaelManaged object initialized in the constructor
    /// </summary>
    public void Dispose()
    {
        if (rijn != null)
            rijn.Dispose();
    }//end Dispose()
}//end class

そして..

これがテストサンプルです。

class Program
{
    string key;
    static void Main(string[] args)
    {
        Program p = new Program();

        //get 16 byte key (just demo - typically you will have a predetermined key)
        p.key = AnotherAES.GetUniqueString(16);

        string plainText = "Hello World!";

        //encrypt
        string hex = p.Encrypt(plainText);

        //decrypt
        string roundTrip = p.Decrypt(hex);

        Console.WriteLine("Round Trip: {0}", roundTrip);
    }

    string Encrypt(string plainText)
    {
        Console.WriteLine("\nSending (encrypt side)...");
        Console.WriteLine("Plain Text: {0}", plainText);
        Console.WriteLine("Key: {0}", key);
        string hex = string.Empty;
        string ivString = AnotherAES.GetUniqueString(16);
        Console.WriteLine("IV: {0}", ivString);
        using (AnotherAES aes = new AnotherAES(key))
        {
            //encrypting side
            byte[] IV = Encoding.UTF8.GetBytes(ivString);

            //get encrypted bytes (IV bytes prepended to cipher bytes)
            byte[] encryptedBytes = aes.Encrypt(plainText, IV);
            byte[] encryptedBytesWithIV = IV.Concat(encryptedBytes).ToArray();

            //get hex string to send with url
            //this hex has both IV and ciphertext
            hex = BitConverter.ToString(encryptedBytesWithIV).Replace("-", "");
            Console.WriteLine("sending hex: {0}", hex);
        }

        return hex;
    }

    string Decrypt(string hex)
    {
        Console.WriteLine("\nReceiving (decrypt side)...");
        Console.WriteLine("received hex: {0}", hex);
        string roundTrip = string.Empty;
        Console.WriteLine("Key " + key);
        using (AnotherAES aes = new AnotherAES(key))
        {
            //get bytes from url
            byte[] encryptedBytesWithIV = AnotherAES.StringToByteArray(hex);

            byte[] IV = encryptedBytesWithIV.Take(16).ToArray();

            Console.WriteLine("IV: {0}", System.Text.Encoding.Default.GetString(IV));

            byte[] cipher = encryptedBytesWithIV.Skip(16).ToArray();

            roundTrip = aes.Decrypt(cipher, IV);
        }
        return roundTrip;
    }
}

ここに画像の説明を入力してください


-2

これは世界で最も単純なものだと思います!

string encrypted = "Text".Aggregate("", (c, a) => c + (char) (a + 2));

テスト

 Console.WriteLine(("Hello").Aggregate("", (c, a) => c + (char) (a + 1)));
            //Output is Ifmmp
 Console.WriteLine(("Ifmmp").Aggregate("", (c, a) => c + (char)(a - 1)));
            //Output is Hello

ROT ... 1?本当に?OPはROT13を、彼やりたくないことの例としてさえ呼びました。
user812786
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.