[英]Converting C# cryptography to Java
我的任务是将C#加密方法转换为Java并且卡住了。 我知道C#代码可以工作,但是我无法让我的Java代码工作。
这是C#代码:
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);
}
这是我到目前为止在Java中的尝试:
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);
}
更多信息
对于GetIV
和GetKey
,我可以保证java中的结果是相同的(我比较了每个字节),但我不包括那些方法,因为我认为这是敏感信息。 我还可以保证输入字节[]是相同的并且(冗余地)相同的长度。
调试尝试: Java中的当前错误是ECB mode cannot use IV
。
new IvParameterSpec(getIV())
我收到此错误: Wrong algorithm: AES or Rijndael required
AES
或仅Rijndael
我得到此错误: Input length must be multiple of 16 when decrypting with padded cipher
。 读取过去/删除前4个字节后,输入长度开始为424
和420
。 我已经验证了Java和C#的输入字节是相同的。 我在Java代码中哪里出错了?
您收到错误ECB mode cannot use IV
因为ECB不执行链接,因此IV无意义。 不同之处是Java会抛出错误而C#只会忽略IV。
当我删除此代码时:
new IvParameterSpec(getIV())
我收到此错误:Wrong algorithm:
required
AES or Rijndael
如果我将算法更改为仅AES或仅Rijndael我得到此错误:
Input length must be multiple of 16 when decrypting with
padded cipher.
Input length must be multiple of 16 when decrypting with
padded cipher.
你有正确的想法,但你走得太远了。 此错误仅与SecretKeySpec
,后者不关心模式,只关心算法。 Cipher
是您指定模式的地方。 此外,Rijndael和AES并不完全相同。
首先,将前几行更改为:
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);
请注意,由于您使用的是整个key
,因此您不需要offset和length参数,因此您可以这样做
SecretKey secretKey = new SecretKeySpec(key, "Rijndael");
原始的C#代码有一些不那么明显的行为:
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);
}
当循环到达input
的末尾时,它将复制,但是它会留下多少进入buffer
。 除非最后一次Read
恰好是1024个字节,否则在input
结束后,前一个循环(或初始化,如果它通过一次Read
操作得到整个input
)将有残留。
内循环一次解密一个16字节的块。 对于420字节的示例,最后一个块将包含剩余的4个字节的输入和12个字节的垃圾 。 但它没关系,因为output.Write
只写bytes
数字节来截断垃圾。 您必须在Java代码中复制此行为。
旁注:你绝对必须使用ECB吗? 它不是很安全......
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.