将 C# 密码学转换为 Java
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中的结果是一样的(每个字节我都比较过),但我不是包括那些方法,因为我认为那是敏感信息。我还可以保证输入 byte[] 是相同的并且(冗余地)相同的长度。
调试尝试: 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
。在读取 past/removing 前 4 个字节后,输入长度开始为 424
和 420
。我已经验证 Java 和 C# 的输入字节相同。
Java 代码哪里出错了?
您收到错误 ECB mode cannot use IV
因为 ECB 不执行链接,所以 IV 没有意义。不同之处在于 Java 会引发错误,而 C# 只会忽略 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.
你的想法是对的,但你做得太过分了。此错误仅与 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 代码中复制此行为。
旁注:您一定要使用 ECB 吗?不是很安全...
我的任务是将 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[] 是相同的并且(冗余地)相同的长度。调试尝试: 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
。在读取 past/removing 前 4 个字节后,输入长度开始为424
和420
。我已经验证 Java 和 C# 的输入字节相同。
- 当我删除此代码时:
Java 代码哪里出错了?
您收到错误 ECB mode cannot use IV
因为 ECB 不执行链接,所以 IV 没有意义。不同之处在于 Java 会引发错误,而 C# 只会忽略 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.
你的想法是对的,但你做得太过分了。此错误仅与 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 代码中复制此行为。
旁注:您一定要使用 ECB 吗?不是很安全...