2

While debugging the performance of my C# application I noticed that it's a lot slower than it's Java equivalence. After looking into it the problem seems to be caused by the encryption/decryption methods.

I am forced to use AES encryption with mode set to CFB8 and no Padding. For Java this is quite straight forward as I can use Cipher.getInstance("AES/CFB8/NoPadding");. In C# I found out that I need to use new RijndaelManaged(). After running tests with the same keys and same data it here are the results:

Java:

  • Encrypt: 0.402s
  • Decrypt: 0.480s

C#:

  • Encrypt: 4.201s
  • Decrypt: 3.671s

C# Cipher code:

    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# Test code:

 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();
    }

Result: "AesCrypto.Crypt took: 3626"

JAVA Cipher code:

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 Test code:

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();
}

Result: "Java Encryption took: PT0.469S"

Possible solution:

After researching this quite a bit it seems that AesCryptoServiceProvider() has around the same performance as Java's equivalence and has a mostly identical results. The problem with this is however that it requires padding, whereas Java's equivalence does not require padding. For example this means if I want to encrypt "abcdab" it would only encrypt "abcd" and return the result for that and keep the rest ("ab") internally. If I use padding I can get it to return the full "abcdab" encrypted, however then it has additional data appended and the symmetric algorithm is de-synced, because in java I could encrypt "abcdab" without any padding.

Question

So finally my question is how would could I make the C# encryption/decryption process just as fast as Java's? Am I doing something wrong with AesCryptoServiceProvider, maybe it's possible for it to not require padding?

Community
  • 1
  • 1
Jpm100
  • 21
  • 2
  • If you want to use AES you should either use `AesManaged` or the `Aes` class. Similarly, if you want to stay at Rijndael, which is a superset of AES and not exactly AES, try to use the `Rijndael` class instead of `RijndaelManaged`. The classes without the Managed suffix may be faster, because it uses OS functionality. Also how does you encryption and decryption in C# look like? – ckuri Feb 17 '19 at 07:03
  • @ckuri I have updated the post to now include the full classes for both java and c#. Also included the test code and the result for each test code. I also tried out `Rijndael` instead of `RijndaelManaged` but the result seems to be the same. I'm just confused why java runs so much fast, even though it uses the Rijndael algorithm. – Jpm100 Feb 17 '19 at 12:58
  • Also as a quick note I'd like to mention that I would `AesManaged` or `Aes` as they match the speed of Java's encryption, however they require padding (which I can't add as the protocol requires me to use the symmetrical encryption against java's implementation), where as `RijndaelManaged` does not require padding, so it acts like Java's Cipher. I'm not sure if I can add some sort of padding to `Aes` that wouldn't break the Symmetric aspect between Java and C#. – Jpm100 Feb 17 '19 at 13:17
  • I'm incredibly confused as to why you can't just create an `AesCryptoServiceProvider` and set its `Padding` property to `PaddingMode.None`. – Ian Kemp Feb 17 '19 at 15:11
  • @IanKemp If I have `AesCryptoServiceProvider` with padding set to None and try to encrypt 9mb (9,000,001 bytes) then I will get an exception (`TransformBlock may only process bytes in block sized increments.`), whereas in Java's Aes crypto that would work without any errors. Am I doing something wrong? Is this not the intended behavior of `AesCryptoServiceProvider`, if so I can post my `AesCryptoServiceProvider` code as well. – Jpm100 Feb 17 '19 at 15:19

0 Answers0