5

次の(トリミングされた)クラスを使用して、iPadアプリからWCFWebサービスにデータを送信する前に一部のデータを暗号化します。

public class FlawedAlgorithm
{
    protected static byte[] key = { 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42 };
    protected static byte[] vector = { 13, 37, 13, 37, 13, 37, 13, 37, 13, 37, 13, 37, 13, 37, 13, 37 };
    protected ICryptoTransform encryptor, decryptor;
    protected UTF8Encoding encoder;

    public FlawedAlgorithm()
    {
        using (var rijndael = new RijndaelManaged())
        {
            this.encryptor = rijndael.CreateEncryptor(key, vector);
            this.decryptor = rijndael.CreateDecryptor(key, vector);
        }

        this.encoder = new UTF8Encoding();
    }

    public string Encrypt(string unencrypted)
    {
        var buffer = this.encoder.GetBytes(unencrypted);

        return Convert.ToBase64String(Encrypt(buffer));
    }

    public string Decrypt(string encrypted)
    {
        var buffer = Convert.FromBase64String(encrypted);

        return this.encoder.GetString(Decrypt(buffer));
    }

    private byte[] Encrypt(byte[] buffer)
    {
        var encryptStream = new MemoryStream();

        using (var cryptoStream = new CryptoStream(encryptStream, this.encryptor, CryptoStreamMode.Write))
        {
            cryptoStream.Write(buffer, 0, buffer.Length);
        }

        return encryptStream.ToArray();
    }

    private byte[] Decrypt(byte[] buffer)
    {
        var decryptStream = new MemoryStream();

        using (var cryptoStream = new CryptoStream(decryptStream, this.decryptor, CryptoStreamMode.Write))
        {
            cryptoStream.Write(buffer, 0, buffer.Length);
        }

        return decryptStream.ToArray();
    }
}

サーバーとiPadで次のコードを実行すると、どちらも同じ暗号化された文字列を出力します。

var algorithm = new FlawedAlgorithm();

Console.WriteLine(algorithm.Encrypt("Some string"));

ただし、2番目の値を暗号化しようとすると、サーバーとiPadでの結果が異なります。

var algorithm = new FlawedAlgorithm();

// The first encryption still functions correctly.
Console.WriteLine(algorithm.Encrypt("Some string"));

// This second encryption produces a different value on the iPad.
Console.WriteLine(algorithm.Encrypt("This text is a bit longer"));

サーバー上で逸脱したiPadの結果を復号化すると、復号化された文字列の一部がぎこちないものになります。サーバーからの暗号化された結果は正しく復号化されます。

FlawedAlgorithm呼び出しごとに新しいインスタンスを作成しても、問題は発生しません。例:

// These statements produce the correct results on the iPad.
Console.WriteLine(new FlawedAlgorithm().Encrypt("Some string"));
Console.WriteLine(new FlawedAlgorithm().Encrypt("This text is a bit longer"));

これは、問題が関係するオブジェクトの状態のどこかにあると私に思わせます。メソッドbuffer内の変数を調べましたが、インスタンスによって生成された値は正しいです。これは、フィールド(またはその基礎となる実装)が原因であることを意味します。Encrypt(string)UTF8Encodingencryptor

最初の暗号化された値のサイズを変更し始めると、2番目の暗号化呼び出しの結果に変化が見られます。これはおそらく、ストリームの一部が適切にクリアまたは上書きされていないことを意味します。FlawedAlgorithmただし、クラスが使用するストリームは、その状態の一部ではありません。これらは、メソッド呼び出しごとに再作成されます。そして、encryptorオブジェクトはそれ自身のストリームを管理するタイプのようには見えません。

他の誰かがこれに似た問題に遭遇しましたか?RijndaelManagedクラスに欠陥がありますか?または、この暗号化の例とは関係のない、MonoTouchのストリームおよびメモリ管理の落とし穴がここで発生していますか?

PS:私はこれをiPadとiPadシミュレーターの両方でテストしました。どちらもこの奇妙な動作を示します。

4

1 に答える 1

8

.NET暗号化を使用する場合は、常にICryptoTransform.CanReuseTransformをチェックする必要があります(またはfalseを返すと想定します)。falseが返された場合、同じ暗号化/復号化を再利用できず、新しいインスタンスを作成する必要があります。

このチェックをスキップすると、フレームワークでの変更(または暗号化はプラグイン可能であるため、構成ファイルを介した変更)により、将来的にアプリケーションが破損する可能性があります。

次のようなものを使用できます。

 ICryptoTransform Decryptor {
    get {
       if (decryptor == null || !decryptor.CanReuseTransform)
          decryptor = rijndael.CreateDecryptor (key, vector);
        return decryptor;
    }
 }

この複雑さを暗号化ルーチンの呼び出し元から隠すため。

于 2011-08-11T15:35:07.927 に答える