在调试我的 C# 应用程序的性能时,我注意到它比等效的 Java 慢很多。经过调查,问题似乎是由加密/解密方法引起的。
我被迫使用模式设置为 CFB8 且没有填充的 AES 加密。对于 Java,这非常简单,因为我可以使用Cipher.getInstance("AES/CFB8/NoPadding");
. 在 C# 中,我发现我需要使用new RijndaelManaged()
. 使用相同的键和相同的数据运行测试后,结果如下:
爪哇:
- 加密:0.402s
- 解密:0.480s
C#:
- 加密:4.201s
- 解密:3.671s
C#密码:
public ICryptoTransform enc;
public ICryptoTransform dec;
public AesCrypto(byte[] key)
{
enc = Generate(key).CreateEncryptor();
dec = Generate(key).CreateDecryptor();
}
private SymmetricAlgorithm Generate(byte[] key) {
RijndaelManaged cipher = new RijndaelManaged();
cipher.Mode = CipherMode.CFB;
cipher.Padding = PaddingMode.None;
cipher.KeySize = 128;
cipher.FeedbackSize = 8;
cipher.Key = key;
cipher.IV = key;
return cipher;
}
public byte[] Crypt(byte[] buffer, int offset, int count) {
return enc.TransformFinalBlock(buffer, offset, count);
}
C#测试代码:
static void Test() {
// Init
var AesCrypto = new AesCrypto(Encoding.UTF8.GetBytes("aaabbbccaaabbbcc"));
var testData = Encoding.UTF8.GetBytes(createDataSize(9000000)); // 9mb test.
// Timer
var stopWatch = new Stopwatch();
stopWatch.Start();
AesCrypto.Crypt(testData, 0, testData.Length);
stopWatch.Stop();
Console.WriteLine("AesCrypto.Crypt took: " + stopWatch.ElapsedMilliseconds);
}
static string createDataSize(int msgSize)
{
StringBuilder sb = new StringBuilder(msgSize);
for (int i = 0; i < msgSize; i++)
{
sb.Append('a');
}
return sb.ToString();
}
结果:“AesCrypto.Crypt 占用:3626”
JAVA密码:
public Cryptor(boolean reader) throws CryptingException {
keySpec = new SecretKeySpec(secretKey.getBytes(CHARSET), "AES");
ivSpec = new IvParameterSpec(iv.getBytes(CHARSET));
try {
cipher = Cipher.getInstance("AES/CFB8/NoPadding");
if (reader) cipher.init(Cipher.DECRYPT_MODE, keySpec, ivSpec);
else cipher.init(Cipher.ENCRYPT_MODE, keySpec, ivSpec);
} catch (NoSuchAlgorithmException e) {
throw new SecurityException(e);
} catch (NoSuchPaddingException e) {
throw new SecurityException(e);
}catch (InvalidKeyException e) {
throw new SecurityException(e);
} catch (InvalidAlgorithmParameterException e) {
throw new SecurityException(e);
}
}
public byte[] decrypt(byte[] input) throws CryptingException {
return cipher.update(input);
}
public byte[] encrypt(String input) throws CryptingException {
return cipher.update(input.getBytes());
}
Java测试代码:
private static void Test() {
// Init
String data = createDataSize(9392963);
Cryptor writer = new Cryptor(false);
// Timer
Instant starts = Instant.now();
byte[] encrypted = writer.encrypt(data);
Instant ends = Instant.now();
System.out.println("Java Encryption took: " + Duration.between(starts, ends));
}
private static String createDataSize(int msgSize) {
StringBuilder sb = new StringBuilder(msgSize);
for (int i=0; i<msgSize; i++) {
sb.append('a');
}
return sb.toString();
}
结果:“Java 加密:PT0.469S”
可能的解决方案:
在对此进行了相当多的研究之后,似乎它AesCryptoServiceProvider()
的性能与 Java 的等价物大致相同,并且结果几乎相同。然而,这样做的问题是它需要填充,而 Java 的等价物不需要填充。例如,这意味着如果我想加密“abcdab”,它只会加密“abcd”并返回结果并将其余部分(“ab”)保留在内部。如果我使用填充,我可以让它返回完整的“abcdab”加密,但是它附加了额外的数据并且对称算法被取消同步,因为在java中我可以加密“abcdab”而没有任何填充。
问题
所以最后我的问题是如何使 C# 加密/解密过程与 Java 一样快?我做错了什么AesCryptoServiceProvider
,也许它可能不需要填充?