Во время отладки производительности моего приложения на C # я заметил, что оно намного медленнее, чем его эквивалентность Java.После рассмотрения проблемы проблема, похоже, вызвана методами шифрования / дешифрования.
Я вынужден использовать шифрование AES с режимом, установленным в CFB8, и без заполнения.Для Java это довольно просто, так как я могу использовать Cipher.getInstance("AES/CFB8/NoPadding");
.В C # я узнал, что мне нужно использовать new RijndaelManaged()
.После запуска тестов с теми же ключами и теми же данными вот результаты :
Java:
- Шифрование: 0,402 с
- Расшифровка: 0,480 с
C #:
- Шифрование: 4.201 с
- Дешифрование: 3,671 с
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
, может быть, для него не требуется заполнение?