Reputation: 7383
I have been tasked with converting a C# cryptography method to Java and am stuck. I know the C# codes works, but I am having trouble getting my Java code to work.
Here is the C# code:
private const int Nb = 4; // Legal values: 4 = 128-bit blocks
public static void Decrypt(byte[] input, Stream output)
{
var s1 = new MemoryStream(input);
const int BufferSize = 1024;
byte[] buffer = new byte[BufferSize];
input.Read(buffer, 0, 4);
int pad = buffer[3];
RijndaelManaged rijndael = new RijndaelManaged();
rijndael.BlockSize = Nb * 32;
rijndael.KeySize = buffer[1] * 32;
rijndael.Mode = CipherMode.ECB;
rijndael.Padding = PaddingMode.None;
byte[] key = GetKey(buffer[1]);
ICryptoTransform decryptor = rijndael.CreateDecryptor(key, GetIV());
int bytes;
while ((bytes = input.Read(buffer, 0, BufferSize)) > 0)
{
for (int i = 0; i < bytes; i += rijndael.BlockSize)
{
decryptor.TransformBlock(buffer, i, rijndael.BlockSize, buffer, i);
}
output.Write(buffer, 0, bytes);
}
output.SetLength(output.Length - pad - 4);
}
And here is my attempt in Java so far:
public static String decrypt(byte[] input) throws Exception {
Cipher cipher = Cipher.getInstance("AES/ECB/NoPadding");
byte[] key = getKey(input[1]);
SecretKey secretKey = new SecretKeySpec(key, 0, key.length, "AES/ECB/NoPadding");
cipher.init(Cipher.DECRYPT_MODE, secretKey, new IvParameterSpec(getIV()));
// remove first 4 since C# code reads past those
byte[] finalDecoded = Arrays.copyOfRange(input, 4, input.length);
byte[] decryptedVal = cipher.doFinal(finalDecoded);
return new String(decryptedVal);
}
Some More Information
For GetIV
and GetKey
, I can guarantee that the results in java are the same (I have compared every byte), but I am not including those methods because I believe that is sensitive information. I can also guarantee that the input byte[] is identical and (redundantly) the same length.
Debugging attempts: The current error in Java is ECB mode cannot use IV
.
new IvParameterSpec(getIV())
I get this error: Wrong algorithm: AES or Rijndael required
AES
or only Rijndael
I get this error: Input length must be multiple of 16 when decrypting with padded cipher
. The input length starting is 424
and 420
after reading past/removing the first 4 bytes. I have verified that the input bytes are the same for Java and C#.Where am I going wrong in the Java code?
Upvotes: 4
Views: 423
Reputation: 359
You are getting the error ECB mode cannot use IV
because ECB doesn't perform chaining, so IV is meaningless. The difference is Java throws an error whereas C# just ignores the IV.
When I remove this code :
new IvParameterSpec(getIV())
I get this error:Wrong algorithm:
AES or Rijndael
required
If I change the algorithm to only AES or only Rijndael I get this error:
Input length must be multiple of 16 when decrypting with
padded cipher.
You had the right idea, but you went too far. This error is only to do with the SecretKeySpec
, which doesn't care about the mode, but just the algorithm. Cipher
is where you specify mode. Also, Rijndael and AES aren't quite the same thing.
So start by changing the first few lines to this:
Cipher cipher = Cipher.getInstance("Rijndael/ECB/NoPadding");
byte[] key = getKey(input[1]);
SecretKey secretKey = new SecretKeySpec(key, 0, key.length, "Rijndael");
cipher.init(Cipher.DECRYPT_MODE, secretKey);
Note that since you're using the entire key
you don't need the offset and length arguments, so you can just do
SecretKey secretKey = new SecretKeySpec(key, "Rijndael");
The original C# code has some not-so-obvious behavior:
while ((bytes = input.Read(buffer, 0, BufferSize)) > 0)
{
for (int i = 0; i < bytes; i += rijndael.BlockSize)
{
decryptor.TransformBlock(buffer, i, rijndael.BlockSize, buffer, i);
}
output.Write(buffer, 0, bytes);
}
When the loop gets to the end of the input
, it will copy however much is left of it into buffer
. Unless the last Read
was exactly 1024 bytes, there will be residue from the previous loop (or from initialization if it gets the whole input
with one Read
operation) after the end of the input.
The inner loop decrypts one 16-byte block at a time. In the case of your 420-byte example, the last block will consist of the remaining 4 bytes of input and 12 more bytes of garbage. But it's okay because the output.Write
only writes bytes
number of bytes to truncate the garbage. You will have to replicate this behavior in your Java code.
Side note: do you absolutely have to use ECB? It's not very secure...
Upvotes: 6