I'm using the following (trimmed-down) class to encrypt some data before sending it from an iPad app to a WCF web service.
我使用下面的(精简)类在将数据从iPad应用程序发送到WCF web服务之前对其进行加密。
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();
}
}
When I run the following code on the server and the iPad, both print the same encrypted string.
当我在服务器和iPad上运行以下代码时,它们都打印相同的加密字符串。
var algorithm = new FlawedAlgorithm();
Console.WriteLine(algorithm.Encrypt("Some string"));
However, when I try to encrypt a second value, the results on the server and iPad are different.
但是,当我尝试加密第二个值时,服务器和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"));
When I decrypt the deviating iPad result on the server, part of the decrypted string is gibberish. The encrypted results from the server decrypt correctly.
当我在服务器上解密iPad的异常结果时,部分解密的字符串是胡言乱语。来自服务器的加密结果正确解密。
The problem does not manifest itself if I create a new FlawedAlgorithm
instance for each call, e.g.:
如果我为每个调用创建一个新的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"));
This leads me to think that the problem lies somewhere in the state of the objects involved. I have inspected the buffer
variable in the Encrypt(string)
method and the values produced by the UTF8Encoding
instance are correct. This implies that the encryptor
field (or its underlying implementation) is the culprit.
这使我认为问题在于所涉及对象的状态。我在Encrypt(string)方法中检查了缓冲区变量,UTF8Encoding实例生成的值是正确的。这意味着encryptor字段(或其底层实现)是罪魁祸首。
When I start varying the size of the first encrypted value, I can see changes in the result of the second encryption call. This would probably mean that some part of a stream isn't being cleared or overwritten properly. But the streams the FlawedAlgorithm
class uses, aren't part of its state; they are recreated on each method call. And the encryptor
object doesn't seem like the type that manages its own streams.
当我开始改变第一个加密值的大小时,我可以看到第二个加密调用结果的变化。这可能意味着流的某些部分没有被正确地清除或覆盖。但是FlawedAlgorithm类使用的流不是它的状态;在每个方法调用上重新创建它们。encryptor对象看起来不像管理自己流的类型。
Has anyone else encountered a problem similar to this? Is the RijndaelManaged
class flawed? Or are there some stream and memory management pitfalls in MonoTouch at play here, unrelated to this cryptography example?
有人遇到过类似的问题吗?RijndaelManaged类有缺陷吗?或者在MonoTouch中是否存在一些流和内存管理缺陷,与这个密码学示例无关?
P.S.: I have tested this on both the iPad and the iPad Simulator; both display this strange behavior.
注::我在iPad和iPad模拟器上都做过测试;两者都表现出这种奇怪的行为。
1 个解决方案
#1
7
When using .NET cryptography you must always check ICryptoTransform.CanReuseTransform (or assume it will return false). If it returns false then you cannot reuse the same encryptor/decryptor and must create new instances.
在使用。net密码时,必须始终检查ICryptoTransform。CanReuseTransform(或者假设它将返回false)。如果返回false,则不能重用相同的encryptor/decryptor,并且必须创建新的实例。
Skipping this check means that any changes in the framework (or via configuration files, since cryptography is pluggable) will likely break your application in the future.
跳过此检查意味着框架中的任何更改(或通过配置文件,因为密码学是可插入的)可能会在将来破坏您的应用程序。
You can use something like:
你可以用这样的方法:
ICryptoTransform Decryptor {
get {
if (decryptor == null || !decryptor.CanReuseTransform)
decryptor = rijndael.CreateDecryptor (key, vector);
return decryptor;
}
}
to hide this complexity from the caller of your cryptographic routines.
向密码例程的调用者隐藏这种复杂性。
#1
7
When using .NET cryptography you must always check ICryptoTransform.CanReuseTransform (or assume it will return false). If it returns false then you cannot reuse the same encryptor/decryptor and must create new instances.
在使用。net密码时,必须始终检查ICryptoTransform。CanReuseTransform(或者假设它将返回false)。如果返回false,则不能重用相同的encryptor/decryptor,并且必须创建新的实例。
Skipping this check means that any changes in the framework (or via configuration files, since cryptography is pluggable) will likely break your application in the future.
跳过此检查意味着框架中的任何更改(或通过配置文件,因为密码学是可插入的)可能会在将来破坏您的应用程序。
You can use something like:
你可以用这样的方法:
ICryptoTransform Decryptor {
get {
if (decryptor == null || !decryptor.CanReuseTransform)
decryptor = rijndael.CreateDecryptor (key, vector);
return decryptor;
}
}
to hide this complexity from the caller of your cryptographic routines.
向密码例程的调用者隐藏这种复杂性。