locked
RijndaelManaged terrible performance when compared to Java. RRS feed

  • Question

  • I have been currently investigating a performance bottleneck and found out that "RijndaelManaged" is causing it. I have an application that decrypts data that is encrypted by Java. The Java cipher is instantiated with the following parameters: "AES/CFB8/NoPadding" and I have found that "RijndaelManaged" is supposed to be used to decrypt this on c#'s side.

    Here is my C# crypto class 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"

    As you can see to decode 9mb of data it took 3.63 seconds, which is insane when compared to Java. The java equivalence took about 0.406 seconds. Here is the Java side crypto 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 crypto is around 10 times faster than the C# equivalence. What am I doing wrong? Is there even a solution to this?

    * Side node, I have tried to use `AesCryptoServiceProvider` instead, however it requires the data size to be a multiple of 16 as I am not allowed to use any padding. `AesCryptoServiceProvider` has slightly faster performance than Java, and would be a perfect solution, however unlike Java's solution it requires the data's size to be a multiple of 16.

    Sunday, February 17, 2019 4:42 PM

All replies

  • Hi RijndaelManaged terrible performance vs Java,

    Thank you for posting here.

    For your question, normally, the encryption speed of the .Net Framework APIs are almost twice as the speed of those in the Java APIs. However, in Rijndael algorithm, jave is more quick than C#.

    Please check the link below.

    https://www.tandfonline.com/doi/full/10.1080/10658980701784602

    Best Regards,

    Wendy

    Note: This response contains a reference to a third-party World Wide Web site. Microsoft is providing this information as a convenience to you. 
    Microsoft does not control these sites and has not tested any software or information found on these sites; Therefore, Microsoft cannot make any representations regarding the quality, safety, or suitability of any software or information found there.
    There are inherent dangers in the use of any software found on the Internet, and Microsoft cautions you to make sure that you completely understand the risk before retrieving any software from the Internet. 


    MSDN Community Support
    Please remember to click "Mark as Answer" the responses that resolved your issue, and to click "Unmark as Answer" if not. This can be beneficial to other community members reading this thread. If you have any compliments or complaints to MSDN Support, feel free to contact MSDNFSF@microsoft.com.

    Monday, February 18, 2019 5:13 AM
  • Thank you for your response. The website you have linked indicates that the .Net version should be only around 15% than the Java version (https://www.tandfonline.com/na101/home/literatum/publisher/tandf/journals/content/uiss19/2007/uiss19.v016.i06/10658980701784602/production/images/medium/uiss_a_278486_o_f0003g.gif), however my results are quite different: Java encrypts a 9mb file in ~0.4 second, while C# encrypts the same sized file in ~4 seconds, which is 10 times slower.

    I have experimented with `AesCryptoServiceProvider` and that seems to match Java's crypto speed, however there seems to be a slight difference in the implementations between Java's aes and .Net's.
    In Java I am able to encrypt "abc" with Code 1, where as in .Net I get an exception: "TransformBlock may only process bytes in block sized increments." as "abc" is of length 3 and .Net Framework's aes requires the block size to be a multiple of 16. Both Java and .Net cryptos are set to No Padding. Is there a way to make `AesCryptoServiceProvider` act the same way as in Java, where if my data's length isn't a multiplicity of 16, it would still work and encrypt the data? Right now it would either throw an exception or store the bytes in a buffer, whereas Java's code instantly returns the encrypted bytes. (Note: I am not allowed to modify the Java code, therefore I cannot use any padding)
    I have attached my C# code (Figure 2) and Java code (Figure 1) below. 
    Thanks

    Figure 1: (Java code)

    // Cryptor class 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()); } // perform test: private static void Test() { // Init String data = "abc"; Cryptor writer = new Cryptor(false); // Timer Instant starts = Instant.now(); byte[] encrypted = writer.encrypt(data);
    // writer.encrypt returns an array of 3 bytes all encrypted successfully.

    Instant ends = Instant.now(); System.out.println("Java Encryption took: " + Duration.between(starts, ends)); // Result: Java Encryption took: PT0.0S. }




    Figure 2: (C# code)

    // AesCrypto class code:
        public ICryptoTransform enc;
        public ICryptoTransform dec;
    
        public AesCrypto(byte[] key)
        {
            enc = Generate(key).CreateEncryptor();
            dec = Generate(key).CreateDecryptor();
        }
    
        private SymmetricAlgorithm Generate(byte[] key) {
            AesCryptoServiceProvider cipher = new AesCryptoServiceProvider(); 
            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) {
            // Exception thrown here:
            // "TransformBlock may only process bytes in block sized increments."
            // !
            return enc.TransformFinalBlock(buffer, offset, count); 
        }
    
    // Test code:
     static void Test() {
            // Init
            var AesCrypto = new AesCrypto(Encoding.UTF8.GetBytes("aaabbbccaaabbbcc"));
            var testData = Encoding.UTF8.GetBytes("abc");
     }



    Monday, February 18, 2019 2:10 PM
  • Hi RijndaelManaged terrible performance vs Java,

    The difference of data would affect it. We could not make sure how much faster is Java than C# for this

    Sorry for that, our forum do not support java. For java, you could ask in StackOverFlow.

    https://stackoverflow.com/questions/tagged/java

    Best Regards,

    Wendy


    MSDN Community Support
    Please remember to click "Mark as Answer" the responses that resolved your issue, and to click "Unmark as Answer" if not. This can be beneficial to other community members reading this thread. If you have any compliments or complaints to MSDN Support, feel free to contact MSDNFSF@microsoft.com.

    Tuesday, February 19, 2019 7:02 AM