次の(トリミングされた)クラスを使用して、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)
UTF8Encoding
encryptor
最初の暗号化された値のサイズを変更し始めると、2番目の暗号化呼び出しの結果に変化が見られます。これはおそらく、ストリームの一部が適切にクリアまたは上書きされていないことを意味します。FlawedAlgorithm
ただし、クラスが使用するストリームは、その状態の一部ではありません。これらは、メソッド呼び出しごとに再作成されます。そして、encryptor
オブジェクトはそれ自身のストリームを管理するタイプのようには見えません。
他の誰かがこれに似た問題に遭遇しましたか?RijndaelManaged
クラスに欠陥がありますか?または、この暗号化の例とは関係のない、MonoTouchのストリームおよびメモリ管理の落とし穴がここで発生していますか?
PS:私はこれをiPadとiPadシミュレーターの両方でテストしました。どちらもこの奇妙な動作を示します。