我的任务是将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中的结果是相同的(我比较了每个字节),但是我没有包括这些方法,因为我相信这些是敏感的信息。我还可以保证输入的byte[]是相同的和(冗余的)相同的长度。ECB mode cannot use 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`. 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#.
我在Java代码中做错了什么?
发布于 2015-07-14 18:26:00
您将得到错误的ECB mode cannot use IV
,因为欧洲央行不执行链接,所以IV没有意义。不同之处在于,Java抛出了一个错误,而C#只是忽略了IV。
当我删除这个代码:
new IvParameterSpec(getIV())
时,我得到了这个错误:new IvParameterSpec(getIV())
如果我只将算法更改为AES或只有Rijndael,则会得到以下错误:Input length must be multiple of 16 when decrypting with
Input length must be multiple of 16 when decrypting with
你的想法是对的,但你做得太过分了。这个错误只与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
,所以不需要偏移量和长度参数,所以您可以这样做
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字节,否则在输入结束后会有来自前一个循环的残留(或者如果它通过一个Read
操作获得整个input
)。
内环一次解密一个16字节的块.在420个字节的示例中,最后一个块将包含其余4个字节的输入和12个字节的垃圾。但是,这是可以的,因为output.Write
只写入bytes
字节数来截断垃圾。您必须在Java代码中复制此行为。
旁注:你一定要使用欧洲央行吗?不是很安全..。
https://stackoverflow.com/questions/31413244
复制相似问题