1

I am making cross-platform application with server being written in Java, so I ended up with C# for Windows. I finally got through all obstacles like different endianities for BigIntegers in these two languages (Java: big endian, C#: little), so I successfuly made key-exchange as well. Now this is where problem comes, in Java I use AES/CBC/PKCS5Padding for encryption, but in C# there was no PKCS5 available, but as I read in other posts to this topic here on SO, PKCS7 is told to be same as PKCS5 or that Java really uses PKCS7 instead of PKCS5. I don't know what's correct.

So this is the code:

using (System.Security.Cryptography.RijndaelManaged rijndael = new System.Security.Cryptography.RijndaelManaged()) { byte[] iv = new byte[16]; for (int i = 0; i < 16; i++) iv[i] = 0; rijndael.Padding = PaddingMode.PKCS7; rijndael.Mode = CipherMode.CBC; rijndael.Key = Sys.PrivateKey; rijndael.KeySize = 128; rijndael.BlockSize = 128; rijndael.IV = iv; Sys.LogWrite("Decrypt input bytes length: " + buff.Length + ", keyLength: " + Sys.PrivateKey.Length); Sys.LogWriteBuffer("Input bytes", buff); Sys.LogWriteBuffer("Input key", Sys.PrivateKey); ICryptoTransform decryptor = rijndael.CreateDecryptor(rijndael.Key, rijndael.IV); buff=rijndael.CreateDecryptor().TransformFinalBlock(buff, 0, buff.Length); Sys.LogWriteBuffer("buffer: ", buff); } 

Please ignore that IV is always 00 00 00 00, this is only for testing purposes. Data come from server always and key is generated in key-exchange:

Decrypt input bytes length: 32, keyLength: 16 Input bytes: 7C 25 3F 49 9E D5 51 67 E2 86 F9 86 2E C1 8F 22 70 51 65 74 FC 39 2C 52 A6 83 36 B5 9A C7 27 B9 Input key: 1C 13 C2 33 50 57 00 DB FD 60 E2 1C 27 2A A5 00 

If I put it to any online AES decryptor like this http://aes.online-domain-tools.com/ I always get correct result: [[27,{"Established":true}]] But in C# I only get this:

System.Security.Cryptography.CryptographicException: Padding is invalid and cannot be removed. at System.Security.Cryptography.RijndaelManagedTransform.DecryptData(Byte[] inputBuffer, Int32 inputOffset, Int32 inputCount, Byte[]& outputBuffer, Int32 outputOffset, PaddingMode paddingMode, Boolean fLast) at System.Security.Cryptography.RijndaelManagedTransform.TransformFinalBlock(Byte[] inputBuffer, Int32 inputOffset, Int32 inputCount) 

I even tried PaddingMode.None, that didn't throw any exception, but output was just random bytes. What am I doing wrong when even online decryptor can solve it correctly?

Other example input data which should produce same result [[27,{"Established":true}]]:

Decrypt input bytes length: 32, keyLength: 16 Input bytes: 85 5C 55 24 44 B8 77 A5 EF CE E7 A1 45 EC F3 84 2F 8B 74 1F AB D9 BE D0 82 64 BC 0D B0 50 73 63 Input key: B2 21 FA 17 63 E6 4C 25 48 03 84 64 8B 70 05 00 Decrypt input bytes length: 32, keyLength: 16 Input bytes: 8B FC 47 B4 91 05 B7 E1 6C 0E 61 78 D2 51 6B 77 EF 80 30 49 37 05 DA 79 47 52 D1 24 B9 DE A7 F3 Input key: E7 BF E0 AA AC F1 26 42 06 D6 59 44 F9 33 74 63 

Notice that IV is always 16 null bytes.

5
  • I think you should try a working solution like this one AES/CBC/PKCS5Padding Encryption/Decryption in C# and see what you did differently Commented Nov 2, 2015 at 17:46
  • Nope, that doesn't work. There is no string, there is only byte stream byte buff[] which is parsed from TCP Recv. As in example given above (Input bytes, Input key), input data are absolutely valid and decrypting them works even in online AES decryptor provided above and Java client too. Commented Nov 2, 2015 at 19:23
  • Personally I would try and use the streaming API. Just calling TransformFinalBlock might work but read the API specification: "TransformFinalBlock is a special function for transforming the last block or a partial block in the stream. It returns a new array that contains the remaining transformed bytes. A new array is returned, because the amount of information returned at the end might be larger than a single block when padding is added.". Using just this method is hacking, not coding. Commented Nov 2, 2015 at 21:01
  • You say, "Notice that IV is always 16 null bytes." That is insecure, and effectively means that your first block is encrypted in ECB mode, not CBC mode. The IV needs to be random and different for every message for security. Commented Nov 3, 2015 at 12:04
  • and in statement above I say that ignore it's insecure, it's just for testing purposes. Commented Nov 3, 2015 at 16:52

1 Answer 1

2

I finally found the issue and it's unbelievable! Instead of creating decryptor this way:

ICryptoTransform decryptor = rijndael.CreateDecryptor(rijndael.Key, rijndael.IV); 

I should have it created this way:

ICryptoTransform decryptor = rijndael.CreateDecryptor(Sys.PrivateKey, iv); 

So if anyone in future had similar problem, here is already generalized function:

 public static String Decrypt(byte[] buff, byte[] key, byte[] iv) { using (System.Security.Cryptography.RijndaelManaged rijndael = new System.Security.Cryptography.RijndaelManaged()) { rijndael.Padding = PaddingMode.PKCS7; rijndael.Mode = CipherMode.CBC; rijndael.KeySize = 128; rijndael.BlockSize = 128; ICryptoTransform decryptor = rijndael.CreateDecryptor(key, iv); System.IO.MemoryStream memoryStream = new System.IO.MemoryStream(buff); CryptoStream cryptoStream = new CryptoStream(memoryStream, decryptor, CryptoStreamMode.Read); byte[] output = new byte[buff.Length]; int readBytes = cryptoStream.Read(output, 0, output.Length); return System.Text.Encoding.UTF8.GetString(output, 0, readBytes); } } 

Hope it helps.

Sign up to request clarification or add additional context in comments.

1 Comment

You could probably just call CreateDecryptor() and it would work since you already set the key and iv on the rijndael object.